1 /* Copyright (C) 2009 Pierre-Marc Fournier
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2.1 of the License, or (at your option) any later version.
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 #include <sys/types.h>
39 /* return value: 0 = subbuffer is finished, it won't produce data anymore
40 * 1 = got subbuffer successfully
44 #define GET_SUBBUF_OK 1
45 #define GET_SUBBUF_DONE 0
46 #define GET_SUBBUF_DIED 2
48 #define PUT_SUBBUF_OK 1
49 #define PUT_SUBBUF_DIED 0
50 #define PUT_SUBBUF_PUSHED 2
51 #define PUT_SUBBUF_DONE 3
54 char *trace_path
=NULL
;
58 /* Number of active buffers and the mutex to protect it. */
59 int active_buffers
= 0;
60 pthread_mutex_t active_buffers_mutex
= PTHREAD_MUTEX_INITIALIZER
;
61 /* Whether a request to end the program was received. */
62 volatile sig_atomic_t terminate_req
= 0;
64 int get_subbuffer(struct buffer_info
*buf
)
67 char *received_msg
=NULL
;
72 asprintf(&send_msg
, "get_subbuffer %s", buf
->name
);
73 result
= ustcomm_send_request(&buf
->conn
, send_msg
, &received_msg
);
74 if((result
== -1 && (errno
== ECONNRESET
|| errno
== EPIPE
)) || result
== 0) {
75 DBG("app died while being traced");
76 retval
= GET_SUBBUF_DIED
;
80 ERR("get_subbuffer: ustcomm_send_request failed");
85 result
= sscanf(received_msg
, "%as %ld", &rep_code
, &buf
->consumed_old
);
86 if(result
!= 2 && result
!= 1) {
87 ERR("unable to parse response to get_subbuffer");
93 if(!strcmp(rep_code
, "OK")) {
94 DBG("got subbuffer %s", buf
->name
);
95 retval
= GET_SUBBUF_OK
;
97 else if(nth_token_is(received_msg
, "END", 0) == 1) {
98 retval
= GET_SUBBUF_DONE
;
101 else if(!strcmp(received_msg
, "NOTFOUND")) {
102 DBG("For buffer %s, the trace was not found. This likely means it was destroyed by the user.", buf
->name
);
103 retval
= GET_SUBBUF_DIED
;
107 DBG("error getting subbuffer %s", buf
->name
);
111 /* FIMXE: free correctly the stuff */
124 int put_subbuffer(struct buffer_info
*buf
)
127 char *received_msg
=NULL
;
132 asprintf(&send_msg
, "put_subbuffer %s %ld", buf
->name
, buf
->consumed_old
);
133 result
= ustcomm_send_request(&buf
->conn
, send_msg
, &received_msg
);
134 if(result
< 0 && (errno
== ECONNRESET
|| errno
== EPIPE
)) {
135 retval
= PUT_SUBBUF_DIED
;
138 else if(result
< 0) {
139 ERR("put_subbuffer: send_message failed");
143 else if(result
== 0) {
144 /* Program seems finished. However this might not be
145 * the last subbuffer that has to be collected.
147 retval
= PUT_SUBBUF_DIED
;
151 result
= sscanf(received_msg
, "%as", &rep_code
);
153 ERR("unable to parse response to put_subbuffer");
158 if(!strcmp(rep_code
, "OK")) {
159 DBG("subbuffer put %s", buf
->name
);
160 retval
= PUT_SUBBUF_OK
;
162 else if(!strcmp(received_msg
, "NOTFOUND")) {
163 DBG("For buffer %s, the trace was not found. This likely means it was destroyed by the user.", buf
->name
);
164 /* However, maybe this was not the last subbuffer. So
165 * we return the program died.
167 retval
= PUT_SUBBUF_DIED
;
171 DBG("put_subbuffer: received error, we were pushed");
172 retval
= PUT_SUBBUF_PUSHED
;
189 void decrement_active_buffers(void *arg
)
191 pthread_mutex_lock(&active_buffers_mutex
);
193 pthread_mutex_unlock(&active_buffers_mutex
);
196 int create_dir_if_needed(char *dir
)
199 result
= mkdir(dir
, 0777);
201 if(errno
!= EEXIST
) {
210 int is_directory(const char *dir
)
215 result
= stat(dir
, &st
);
221 if(!S_ISDIR(st
.st_mode
)) {
228 struct buffer_info
*connect_buffer(pid_t pid
, const char *bufname
)
230 struct buffer_info
*buf
;
236 struct shmid_ds shmds
;
238 buf
= (struct buffer_info
*) malloc(sizeof(struct buffer_info
));
240 ERR("add_buffer: insufficient memory");
248 result
= ustcomm_connect_app(buf
->pid
, &buf
->conn
);
250 WARN("unable to connect to process, it probably died before we were able to connect");
255 asprintf(&send_msg
, "get_pidunique");
256 result
= ustcomm_send_request(&buf
->conn
, send_msg
, &received_msg
);
259 ERR("problem in ustcomm_send_request(get_pidunique)");
266 result
= sscanf(received_msg
, "%lld", &buf
->pidunique
);
268 ERR("unable to parse response to get_pidunique");
272 DBG("got pidunique %lld", buf
->pidunique
);
275 asprintf(&send_msg
, "get_shmid %s", buf
->name
);
276 result
= ustcomm_send_request(&buf
->conn
, send_msg
, &received_msg
);
279 ERR("problem in ustcomm_send_request(get_shmid)");
286 result
= sscanf(received_msg
, "%d %d", &buf
->shmid
, &buf
->bufstruct_shmid
);
288 ERR("unable to parse response to get_shmid (\"%s\")", received_msg
);
292 DBG("got shmids %d %d", buf
->shmid
, buf
->bufstruct_shmid
);
295 asprintf(&send_msg
, "get_n_subbufs %s", buf
->name
);
296 result
= ustcomm_send_request(&buf
->conn
, send_msg
, &received_msg
);
299 ERR("problem in ustcomm_send_request(g_n_subbufs)");
306 result
= sscanf(received_msg
, "%d", &buf
->n_subbufs
);
308 ERR("unable to parse response to get_n_subbufs");
312 DBG("got n_subbufs %d", buf
->n_subbufs
);
314 /* get subbuf size */
315 asprintf(&send_msg
, "get_subbuf_size %s", buf
->name
);
316 result
= ustcomm_send_request(&buf
->conn
, send_msg
, &received_msg
);
319 ERR("problem in ustcomm_send_request(get_subbuf_size)");
326 result
= sscanf(received_msg
, "%d", &buf
->subbuf_size
);
328 ERR("unable to parse response to get_subbuf_size");
332 DBG("got subbuf_size %d", buf
->subbuf_size
);
335 buf
->mem
= shmat(buf
->shmid
, NULL
, 0);
336 if(buf
->mem
== (void *) 0) {
340 DBG("successfully attached buffer memory");
342 buf
->bufstruct_mem
= shmat(buf
->bufstruct_shmid
, NULL
, 0);
343 if(buf
->bufstruct_mem
== (void *) 0) {
347 DBG("successfully attached buffer bufstruct memory");
349 /* obtain info on the memory segment */
350 result
= shmctl(buf
->shmid
, IPC_STAT
, &shmds
);
355 buf
->memlen
= shmds
.shm_segsz
;
357 /* open file for output */
359 /* Only create the directory if using the default path, because
360 * of the risk of typo when using trace path override. We don't
361 * want to risk creating plenty of useless directories in that case.
363 result
= create_dir_if_needed(USTD_DEFAULT_TRACE_PATH
);
365 ERR("could not create directory %s", USTD_DEFAULT_TRACE_PATH
);
369 trace_path
= USTD_DEFAULT_TRACE_PATH
;
372 asprintf(&tmp
, "%s/%u_%lld", trace_path
, buf
->pid
, buf
->pidunique
);
373 result
= create_dir_if_needed(tmp
);
375 ERR("could not create directory %s", tmp
);
381 asprintf(&tmp
, "%s/%u_%lld/%s", trace_path
, buf
->pid
, buf
->pidunique
, buf
->name
);
382 result
= fd
= open(tmp
, O_WRONLY
| O_CREAT
| O_TRUNC
| O_EXCL
, 00600);
385 ERR("failed opening trace file %s", tmp
);
391 pthread_mutex_lock(&active_buffers_mutex
);
393 pthread_mutex_unlock(&active_buffers_mutex
);
402 static void destroy_buffer(struct buffer_info
*buf
)
406 result
= ustcomm_close_app(&buf
->conn
);
408 WARN("problem calling ustcomm_close_app");
411 result
= shmdt(buf
->mem
);
416 result
= shmdt(buf
->bufstruct_mem
);
421 result
= close(buf
->file_fd
);
429 int unwrite_last_subbuffer(struct buffer_info
*buf
)
433 result
= ftruncate(buf
->file_fd
, buf
->previous_offset
);
439 result
= lseek(buf
->file_fd
, buf
->previous_offset
, SEEK_SET
);
440 if(result
== (int)(off_t
)-1) {
448 int write_current_subbuffer(struct buffer_info
*buf
)
452 void *subbuf_mem
= buf
->mem
+ (buf
->consumed_old
& (buf
->n_subbufs
* buf
->subbuf_size
-1));
454 size_t cur_sb_size
= subbuffer_data_size(subbuf_mem
);
456 off_t cur_offset
= lseek(buf
->file_fd
, 0, SEEK_CUR
);
457 if(cur_offset
== (off_t
)-1) {
462 buf
->previous_offset
= cur_offset
;
463 DBG("previous_offset: %ld", cur_offset
);
465 result
= patient_write(buf
->file_fd
, subbuf_mem
, cur_sb_size
);
474 int consumer_loop(struct buffer_info
*buf
)
478 pthread_cleanup_push(decrement_active_buffers
, NULL
);
481 /* get the subbuffer */
482 result
= get_subbuffer(buf
);
484 ERR("error getting subbuffer");
487 else if(result
== GET_SUBBUF_DONE
) {
491 else if(result
== GET_SUBBUF_DIED
) {
492 finish_consuming_dead_subbuffer(buf
);
496 /* write data to file */
497 result
= write_current_subbuffer(buf
);
499 ERR("Failed writing a subbuffer to file (channel=%s). Dropping this buffer.", buf
->name
);
502 /* put the subbuffer */
503 result
= put_subbuffer(buf
);
505 ERR("unknown error putting subbuffer (channel=%s)", buf
->name
);
508 else if(result
== PUT_SUBBUF_PUSHED
) {
509 ERR("Buffer overflow (channel=%s), reader pushed. This channel will not be usable passed this point.", buf
->name
);
512 else if(result
== PUT_SUBBUF_DIED
) {
513 DBG("application died while putting subbuffer");
514 /* Skip the first subbuffer. We are not sure it is trustable
515 * because the put_subbuffer() did not complete.
517 unwrite_last_subbuffer(buf
);
518 finish_consuming_dead_subbuffer(buf
);
521 else if(result
== PUT_SUBBUF_DONE
) {
522 /* Done with this subbuffer */
523 /* FIXME: add a case where this branch is used? Upon
524 * normal trace termination, at put_subbuf time, a
525 * special last-subbuffer code could be returned by
530 else if(result
== PUT_SUBBUF_OK
) {
534 DBG("thread for buffer %s is stopping", buf
->name
);
536 /* FIXME: destroy, unalloc... */
538 pthread_cleanup_pop(1);
543 struct consumer_thread_args
{
548 void *consumer_thread(void *arg
)
550 struct buffer_info
*buf
= (struct buffer_info
*) arg
;
551 struct consumer_thread_args
*args
= (struct consumer_thread_args
*) arg
;
555 DBG("GOT ARGS: pid %d bufname %s", args
->pid
, args
->bufname
);
557 /* Block signals that should be handled by the main thread. */
558 result
= sigemptyset(&sigset
);
560 PERROR("sigemptyset");
563 result
= sigaddset(&sigset
, SIGTERM
);
568 result
= sigaddset(&sigset
, SIGINT
);
573 result
= sigprocmask(SIG_BLOCK
, &sigset
, NULL
);
575 PERROR("sigprocmask");
579 buf
= connect_buffer(args
->pid
, args
->bufname
);
581 ERR("failed to connect to buffer");
590 free((void *)args
->bufname
);
595 int start_consuming_buffer(pid_t pid
, const char *bufname
)
598 struct consumer_thread_args
*args
;
601 DBG("beginning of start_consuming_buffer: args: pid %d bufname %s", pid
, bufname
);
603 args
= (struct consumer_thread_args
*) malloc(sizeof(struct consumer_thread_args
));
606 args
->bufname
= strdup(bufname
);
607 DBG("beginning2 of start_consuming_buffer: args: pid %d bufname %s", args
->pid
, args
->bufname
);
609 result
= pthread_create(&thr
, NULL
, consumer_thread
, args
);
611 ERR("pthread_create failed");
614 result
= pthread_detach(thr
);
616 ERR("pthread_detach failed");
619 DBG("end of start_consuming_buffer: args: pid %d bufname %s", args
->pid
, args
->bufname
);
626 fprintf(stderr
, "Usage:\nustd OPTIONS\n\nOptions:\n"
627 "\t-h\t\tDisplay this usage.\n"
628 "\t-o DIR\t\tSpecify the directory where to output the traces.\n"
629 "\t-s PATH\t\tSpecify the path to use for the daemon socket.\n"
630 "\t-d\t\tStart as a daemon.\n"
631 "\t--pidfile FILE\tWrite the PID in this file (when using -d).\n");
634 int parse_args(int argc
, char **argv
)
639 int option_index
= 0;
640 static struct option long_options
[] = {
641 {"pidfile", 1, 0, 'p'},
643 {"version", 0, 0, 'V'},
647 c
= getopt_long(argc
, argv
, "hs:o:d", long_options
, &option_index
);
653 printf("option %s", long_options
[option_index
].name
);
655 printf(" with arg %s", optarg
);
663 if(!is_directory(trace_path
)) {
664 ERR("Not a valid directory. (%s)", trace_path
);
672 pidfile
= strdup(optarg
);
678 printf("Version 0.0\n");
682 /* unknown option or other error; error is
683 printed by getopt, just return */
691 void sigterm_handler(int sig
)
696 static int write_pidfile(const char *file_name
, pid_t pid
)
700 pidfp
= fopen(file_name
, "w");
702 PERROR("fopen (%s)", pidfile
);
703 WARN("killing child process");
707 fprintf(pidfp
, "%d\n", pid
);
714 int start_ustd(int fd
)
716 struct ustcomm_ustd ustd
;
722 result
= sigemptyset(&sigset
);
724 PERROR("sigemptyset");
727 sa
.sa_handler
= sigterm_handler
;
730 result
= sigaction(SIGTERM
, &sa
, NULL
);
735 result
= sigaction(SIGINT
, &sa
, NULL
);
741 result
= ustcomm_init_ustd(&ustd
, sock_path
);
743 ERR("failed to initialize socket");
747 /* setup handler for SIGPIPE */
748 result
= sigemptyset(&sigset
);
750 PERROR("sigemptyset");
753 result
= sigaddset(&sigset
, SIGPIPE
);
758 result
= sigprocmask(SIG_BLOCK
, &sigset
, NULL
);
760 PERROR("sigprocmask");
766 result
= write_pidfile(pidfile
, getpid());
768 ERR("failed to write pidfile");
773 /* Notify parent that we are successfully started. */
775 /* write any one character */
776 result
= write(fd
, "!", 1);
782 ERR("Problem sending confirmation of daemon start to parent");
795 /* check for requests on our public socket */
796 result
= ustcomm_ustd_recv_message(&ustd
, &recvbuf
, NULL
, timeout
);
797 if(result
== -1 && errno
== EINTR
) {
799 printf("Caught signal\n");
801 else if(result
== -1) {
802 ERR("error in ustcomm_ustd_recv_message");
805 else if(result
> 0) {
806 if(!strncmp(recvbuf
, "collect", 7)) {
811 result
= sscanf(recvbuf
, "%*s %d %50as", &pid
, &bufname
);
813 ERR("parsing error: %s", recvbuf
);
817 result
= start_consuming_buffer(pid
, bufname
);
819 ERR("error in add_buffer");
827 WARN("unknown command: %s", recvbuf
);
836 pthread_mutex_lock(&active_buffers_mutex
);
837 if(active_buffers
== 0) {
838 pthread_mutex_unlock(&active_buffers_mutex
);
841 pthread_mutex_unlock(&active_buffers_mutex
);
846 ustcomm_fini_ustd(&ustd
);
851 int start_ustd_daemon()
859 result
= child_pid
= fork();
864 else if(result
== 0) {
865 return start_ustd(fd
[1]);
870 result
= read(fd
[0], &buf
, 1);
876 ERR("did not receive valid confirmation that the daemon is started");
880 result
= close(fd
[0]);
885 DBG("The daemon is now successfully started");
888 /* Wait for confirmation that the server is ready. */
894 int main(int argc
, char **argv
)
898 result
= parse_args(argc
, argv
);
904 result
= start_ustd_daemon();
907 result
= start_ustd(-1);