1 /* LTTng user-space "fast" library
3 * This daemon is spawned by each traced thread (to share the mmap).
5 * Its job is to dump periodically this buffer to disk (when it receives a
6 * SIGUSR1 from its parent).
8 * It uses the control information in the shared memory area (producer/consumer
11 * When the parent thread dies (yes, those thing may happen) ;) , this daemon
12 * will flush the last buffer and write it to disk.
14 * Supplement note for streaming : the daemon is responsible for flushing
15 * periodically the buffer if it is streaming data.
19 * shm memory is typically limited to 4096 units (system wide limit SHMMNI in
20 * /proc/sys/kernel/shmmni). As it requires computation time upon creation, we
21 * do not use it : we will use a shared mmap() instead which is passed through
23 * MAP_SHARED mmap segment. Updated when msync or munmap are called.
25 * Memory mapped by mmap() is preserved across fork(2), with the same
28 * Eventually, there will be two mode :
29 * * Slow thread spawn : a fork() is done for each new thread. If the process
30 * dies, the data is not lost.
31 * * Fast thread spawn : a pthread_create() is done by the application for each
34 * We use a timer to check periodically if the parent died. I think it is less
35 * intrusive than a ptrace() on the parent, which would get every signal. The
36 * side effect of this is that we won't be notified if the parent does an
37 * exec(). In this case, we will just sit there until the parent exits.
40 * Copyright 2006 Mathieu Desnoyers
44 #include <sys/types.h>
60 #include <sys/param.h>
61 #include <linux/futex.h>
64 #include <asm/timex.h> //for get_cycles()
66 #include "ltt-usertrace-fast.h"
69 /* Writer (the traced application) */
71 __thread
struct ltt_trace_info
*thread_trace_info
= NULL
;
73 void ltt_usertrace_fast_buffer_switch(void)
75 struct ltt_trace_info
*tmp
= thread_trace_info
;
77 kill(tmp
->daemon_id
, SIGUSR1
);
80 /* The cleanup should never be called from a signal handler */
81 static void ltt_usertrace_fast_cleanup(void *arg
)
83 struct ltt_trace_info
*tmp
= thread_trace_info
;
85 thread_trace_info
= NULL
;
86 kill(tmp
->daemon_id
, SIGUSR2
);
87 munmap(tmp
, sizeof(*tmp
));
91 /* Reader (the disk dumper daemon) */
93 static pid_t traced_pid
= 0;
94 static pthread_t traced_thread
= 0;
95 static int parent_exited
= 0;
98 static void handler_sigusr1(int signo
)
100 printf("LTT Signal %d received : parent buffer switch.\n", signo
);
103 static void handler_sigusr2(int signo
)
105 printf("LTT Signal %d received : parent exited.\n", signo
);
109 static void handler_sigalarm(int signo
)
111 printf("LTT Signal %d received\n", signo
);
113 if(getppid() != traced_pid
) {
115 printf("LTT Parent %lu died, cleaning up\n", traced_pid
);
121 /* Do a buffer switch. Don't switch if buffer is completely empty */
122 static void flush_buffer(struct ltt_buf
*ltt_buf
)
128 static inline int ltt_buffer_get(struct ltt_buf
*ltt_buf
,
129 unsigned int *offset
)
131 unsigned int consumed_old
, consumed_idx
;
132 consumed_old
= atomic_read(<t_buf
->consumed
);
133 consumed_idx
= SUBBUF_INDEX(consumed_old
, ltt_buf
);
135 if(atomic_read(<t_buf
->commit_count
[consumed_idx
])
136 != atomic_read(<t_buf
->reserve_count
[consumed_idx
])) {
139 if((SUBBUF_TRUNC(atomic_read(<t_buf
->offset
), ltt_buf
)
140 -SUBBUF_TRUNC(consumed_old
, ltt_buf
)) == 0) {
144 *offset
= consumed_old
;
149 static inline int ltt_buffer_put(struct ltt_buf
*ltt_buf
,
152 unsigned int consumed_old
, consumed_new
;
155 consumed_old
= offset
;
156 consumed_new
= SUBBUF_ALIGN(consumed_old
, ltt_buf
);
157 if(atomic_cmpxchg(<t_buf
->consumed
, consumed_old
, consumed_new
)
159 /* We have been pushed by the writer : the last buffer read _is_
161 * It can also happen if this is a buffer we never got. */
164 if(atomic_read(<t_buf
->full
) == 1) {
165 /* tell the client that buffer is now unfull */
166 ret
= futex(<t_buf
->full
, FUTEX_WAKE
, 1, NULL
, NULL
, 0);
168 printf("LTT warning : race condition : writer not waiting or too many writers\n");
170 atomic_set(<t_buf
->full
, 0);
177 * if(buffer full condition) {
178 * put myself in the wait queue
184 atomic_set(<t_buf->full, 1);
185 ret = futex(<t_buf->full, 1, NULL, NULL, 0);
191 static int read_subbuffer(struct ltt_buf
*ltt_buf
, int fd
)
194 printf("LTT read buffer\n");
197 err
= ltt_buffer_get(&shared_trace_info
->channel
.cpu
, &consumed_old
);
198 if(err
!= -EAGAIN
&& err
!= 0) {
199 printf("LTT Reserving sub buffer failed\n");
203 err
= TEMP_FAILURE_RETRY(write(fd
,
205 + (consumed_old
& ((ltt_buf
->alloc_size
)-1)),
206 ltt_buf
->subbuf_size
));
209 perror("Error in writing to file");
213 err
= fsync(pair
->trace
);
216 perror("Error in writing to file");
221 err
= ltt_buffer_put(&shared_trace_info
->channel
.cpu
, consumed_old
);
225 perror("Reader has been pushed by the writer, last subbuffer corrupted.");
226 /* FIXME : we may delete the last written buffer if we wish. */
235 /* This function is called by ltt_rw_init which has signals blocked */
236 static void ltt_usertrace_fast_daemon(struct ltt_trace_info
*shared_trace_info
,
237 sigset_t oldset
, pid_t l_traced_pid
, pthread_t l_traced_thread
)
239 struct sigaction act
;
243 char outfile_name
[PATH_MAX
];
244 char identifier_name
[PATH_MAX
];
247 traced_pid
= l_traced_pid
;
248 traced_thread
= l_traced_thread
;
250 printf("LTT ltt_usertrace_fast_daemon : init is %d, pid is %lu, traced_pid is %lu\n",
251 shared_trace_info
->init
, getpid(), traced_pid
);
253 act
.sa_handler
= handler_sigusr1
;
255 sigemptyset(&(act
.sa_mask
));
256 sigaddset(&(act
.sa_mask
), SIGUSR1
);
257 sigaction(SIGUSR1
, &act
, NULL
);
259 act
.sa_handler
= handler_sigusr2
;
261 sigemptyset(&(act
.sa_mask
));
262 sigaddset(&(act
.sa_mask
), SIGUSR2
);
263 sigaction(SIGUSR2
, &act
, NULL
);
265 act
.sa_handler
= handler_sigalarm
;
267 sigemptyset(&(act
.sa_mask
));
268 sigaddset(&(act
.sa_mask
), SIGALRM
);
269 sigaction(SIGALRM
, &act
, NULL
);
272 ret
= pthread_sigmask(SIG_SETMASK
, &oldset
, NULL
);
274 printf("LTT Error in pthread_sigmask\n");
279 /* Open output files */
281 ret
= mkdir(LTT_USERTRACE_ROOT
, 0777);
282 if(ret
< 0 && errno
!= EEXIST
) {
283 perror("LTT Error in creating output (mkdir)");
286 ret
= chdir(LTT_USERTRACE_ROOT
);
288 perror("LTT Error in creating output (chdir)");
291 snprintf(identifier_name
, PATH_MAX
-1, "%lu.%lu.%llu",
292 traced_pid
, traced_thread
, get_cycles());
293 snprintf(outfile_name
, PATH_MAX
-1, "facilities-%s", identifier_name
);
294 fd_fac
= creat(outfile_name
, 0644);
296 snprintf(outfile_name
, PATH_MAX
-1, "cpu-%s", identifier_name
);
297 fd_cpu
= creat(outfile_name
, 0644);
302 if(traced_pid
== 0) break; /* parent died */
303 if(parent_exited
) break;
304 printf("LTT Doing a buffer switch read. pid is : %lu\n", getpid());
307 ret
= read_buffer(&shared_trace_info
->channel
.cpu
, fd_cpu
);
311 ret
= read_buffer(&shared_trace_info
->channel
.facilities
, fd_fac
);
315 /* Buffer force switch (flush) */
316 flush_buffer(&shared_trace_info
->channel
.cpu
);
318 ret
= read_buffer(&shared_trace_info
->channel
.cpu
, fd_cpu
);
322 flush_buffer(&shared_trace_info
->channel
.facilities
);
324 ret
= read_buffer(&shared_trace_info
->channel
.facilities
, fd_fac
);
330 /* The parent thread is dead and we have finished with the buffer */
331 munmap(shared_trace_info
, sizeof(*shared_trace_info
));
337 /* Reader-writer initialization */
339 static enum ltt_process_role
{ LTT_ROLE_WRITER
, LTT_ROLE_READER
}
340 role
= LTT_ROLE_WRITER
;
343 void ltt_rw_init(void)
346 struct ltt_trace_info
*shared_trace_info
;
348 sigset_t set
, oldset
;
349 pid_t l_traced_pid
= getpid();
350 pthread_t l_traced_thread
= pthread_self();
352 /* parent : create the shared memory map */
353 shared_trace_info
= mmap(0, sizeof(*thread_trace_info
),
354 PROT_READ
|PROT_WRITE
, MAP_SHARED
|MAP_ANONYMOUS
, 0, 0);
355 memset(shared_trace_info
, 0, sizeof(*shared_trace_info
));
356 /* Tricky semaphore : is in a shared memory space, so it's ok for a fast
358 atomic_set(&shared_trace_info
->channel
.facilities
.full
, 0);
359 shared_trace_info
->channel
.facilities
.alloc_size
= LTT_BUF_SIZE_FACILITIES
;
360 shared_trace_info
->channel
.facilities
.subbuf_size
= LTT_SUBBUF_SIZE_FACILITIES
;
361 atomic_set(&shared_trace_info
->channel
.cpu
.full
, 0);
362 shared_trace_info
->channel
.cpu
.alloc_size
= LTT_BUF_SIZE_CPU
;
363 shared_trace_info
->channel
.cpu
.subbuf_size
= LTT_SUBBUF_SIZE_CPU
;
364 shared_trace_info
->init
= 1;
366 /* Disable signals */
367 ret
= sigfillset(&set
);
369 printf("LTT Error in sigfillset\n");
373 ret
= pthread_sigmask(SIG_BLOCK
, &set
, &oldset
);
375 printf("LTT Error in pthread_sigmask\n");
381 shared_trace_info
->daemon_id
= pid
;
382 thread_trace_info
= shared_trace_info
;
385 ret
= pthread_sigmask(SIG_SETMASK
, &oldset
, NULL
);
387 printf("LTT Error in pthread_sigmask\n");
389 } else if(pid
== 0) {
391 role
= LTT_ROLE_READER
;
392 ltt_usertrace_fast_daemon(shared_trace_info
, oldset
, l_traced_pid
,
394 /* Should never return */
398 perror("LTT Error in forking ltt-usertrace-fast");
402 static __thread
struct _pthread_cleanup_buffer cleanup_buffer
;
404 void ltt_thread_init(void)
406 _pthread_cleanup_push(&cleanup_buffer
, ltt_usertrace_fast_cleanup
, NULL
);
410 void __attribute__((constructor
)) __ltt_usertrace_fast_init(void)
412 printf("LTT usertrace-fast init\n");
417 void __attribute__((destructor
)) __ltt_usertrace_fast_fini(void)
419 if(role
== LTT_ROLE_WRITER
) {
420 printf("LTT usertrace-fast fini\n");
421 ltt_usertrace_fast_cleanup(NULL
);