From 2f334a851fa1b39cab74724f3d0a0565f86c27b4 Mon Sep 17 00:00:00 2001 From: Stefan Vacek Date: Wed, 26 Aug 2015 14:05:54 +0200 Subject: Allow applications to fork() - if fork() is called from an application, dlt is reset and user application needs to re-register application and contexts in child-process Signed-off-by: Stefan Vacek --- src/lib/dlt_user.c | 220 +++++++++++++++++++++++++++++--------- src/tests/CMakeLists.txt | 7 +- src/tests/dlt-test-fork-handler.c | 47 ++++++++ 3 files changed, 225 insertions(+), 49 deletions(-) create mode 100644 src/tests/dlt-test-fork-handler.c diff --git a/src/lib/dlt_user.c b/src/lib/dlt_user.c index 28c7381..e1f46ca 100644 --- a/src/lib/dlt_user.c +++ b/src/lib/dlt_user.c @@ -68,7 +68,9 @@ static char str[DLT_USER_BUFFER_LENGTH]; static sem_t dlt_mutex; static pthread_t dlt_receiverthread_handle; -static pthread_attr_t dlt_receiverthread_attr; + +// calling dlt_user_atexit_handler() second time fails with error message +static int atexit_registered = 0; /* Segmented Network Trace */ #define DLT_MAX_TRACE_SEGMENT_SIZE 1024 @@ -79,6 +81,26 @@ static pthread_attr_t dlt_receiverthread_attr; pthread_mutex_t mq_mutex; pthread_cond_t mq_init_condition; +void dlt_lock_mutex(pthread_mutex_t *mutex) +{ + int32_t lock_mutex_result = pthread_mutex_lock(mutex); + if (lock_mutex_result == EOWNERDEAD) + { + pthread_mutex_consistent(mutex); + lock_mutex_result = 0; + } + else if ( lock_mutex_result != 0 ) + { + snprintf(str,DLT_USER_BUFFER_LENGTH, "Mutex lock failed unexpected pid=%i with result %i!\n", getpid(), lock_mutex_result); + dlt_log(LOG_ERR, str); + } +} + +inline void dlt_unlock_mutex(pthread_mutex_t *mutex) +{ + pthread_mutex_unlock(mutex); +} + /* Structure to pass data to segmented thread */ typedef struct { DltContext *handle; @@ -110,6 +132,13 @@ static void dlt_user_trace_network_segmented_thread(void *unused); static void dlt_user_trace_network_segmented_thread_segmenter(s_segmented_data *data); static int dlt_user_queue_resend(void); +static int dlt_start_threads(); +static void dlt_stop_threads(); +static void dlt_fork_pre_fork_handler(); +static void dlt_fork_parent_fork_handler(); +static void dlt_fork_child_fork_handler(); + + int dlt_user_check_library_version(const char *user_major_version,const char *user_minor_version){ char str[DLT_USER_BUFFER_LENGTH]; @@ -249,44 +278,38 @@ int dlt_init(void) return -1; } - /* Start receiver thread */ - if (pthread_create(&(dlt_receiverthread_handle), - 0, - (void *) &dlt_user_receiverthread_function, - 0)!=0) - { - if (pthread_attr_destroy(&dlt_receiverthread_attr)!=0) - { - dlt_log(LOG_WARNING, "Can't destroy thread attributes!\n"); - } - - dlt_log(LOG_CRIT, "Can't create receiver thread!\n"); - dlt_user_initialised = 0; - return -1; - } - - if (pthread_attr_destroy(&dlt_receiverthread_attr)!=0) - { - dlt_log(LOG_WARNING, "Can't destroy thread attributes!\n"); - } - /* These will be lazy initialized only when needed */ dlt_user.dlt_segmented_queue_read_handle = -1; dlt_user.dlt_segmented_queue_write_handle = -1; /* Wait mutext for segmented thread */ - pthread_mutex_init(&mq_mutex, NULL); + pthread_mutexattr_t attr; + if (pthread_mutexattr_init(&attr) != 0) + { + dlt_user_initialised = 0; + return -1; + } + /* make mutex robust to prevent from deadlock when the segmented thread was cancelled, but held the mutex */ + if ( pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST) != 0 ) + { + dlt_user_initialised = 0; + return -1; + } + + pthread_mutex_init(&mq_mutex, &attr); + pthread_mutexattr_destroy(&attr); pthread_cond_init(&mq_init_condition, NULL); - /* Start the segmented thread */ - if(pthread_create(&(dlt_user.dlt_segmented_nwt_handle), NULL, - (void *)dlt_user_trace_network_segmented_thread, NULL)) + if (dlt_start_threads() < 0) { - dlt_log(LOG_CRIT, "Can't start segmented thread!\n"); - return -1; + dlt_user_initialised = 0; + return -1; } - return 0; + // prepare for fork() call + pthread_atfork(&dlt_fork_pre_fork_handler, &dlt_fork_parent_fork_handler, &dlt_fork_child_fork_handler); + + return 0; } int dlt_init_file(const char *name) @@ -316,12 +339,12 @@ int dlt_init_file(const char *name) int dlt_init_message_queue(void) { - pthread_mutex_lock(&mq_mutex); + dlt_lock_mutex(&mq_mutex); if(dlt_user.dlt_segmented_queue_read_handle >= 0 && dlt_user.dlt_segmented_queue_write_handle >= 0) { // Already intialized - pthread_mutex_unlock(&mq_mutex); + dlt_unlock_mutex(&mq_mutex); return 0; } @@ -364,7 +387,7 @@ int dlt_init_message_queue(void) char str[256]; snprintf(str,255,"Can't create message queue read handle!: %s \n",strerror(errno)); dlt_log(LOG_CRIT, str); - pthread_mutex_unlock(&mq_mutex); + dlt_unlock_mutex(&mq_mutex); return -1; } } @@ -376,12 +399,12 @@ int dlt_init_message_queue(void) char str[256]; snprintf(str,255,"Can't open message queue write handle!: %s \n",strerror(errno)); dlt_log(LOG_CRIT, str); - pthread_mutex_unlock(&mq_mutex); + dlt_unlock_mutex(&mq_mutex); return -1; } pthread_cond_signal(&mq_init_condition); - pthread_mutex_unlock(&mq_mutex); + dlt_unlock_mutex(&mq_mutex); return 0; } @@ -460,7 +483,11 @@ int dlt_init_common(void) signal(SIGPIPE,SIG_IGN); /* ignore pipe signals */ - atexit(dlt_user_atexit_handler); + if (atexit_registered == 0) + { + atexit(dlt_user_atexit_handler); + atexit_registered = 1; + } #ifdef DLT_TEST_ENABLE dlt_user.corrupt_user_header = 0; @@ -570,16 +597,7 @@ int dlt_free(void) } dlt_user_initialised = 0; - if (dlt_receiverthread_handle) - { - /* Ignore return value */ - pthread_cancel(dlt_receiverthread_handle); - } - - if (dlt_user.dlt_segmented_nwt_handle) - { - pthread_cancel(dlt_user.dlt_segmented_nwt_handle); - } + dlt_stop_threads(); if (dlt_user.dlt_user_handle!=DLT_FD_INIT) { @@ -643,8 +661,14 @@ int dlt_free(void) */ mq_close(dlt_user.dlt_segmented_queue_write_handle); mq_close(dlt_user.dlt_segmented_queue_read_handle); + dlt_user.dlt_segmented_queue_write_handle = -1; + dlt_user.dlt_segmented_queue_read_handle = -1; mq_unlink(queue_name); + pthread_cond_destroy(&mq_init_condition); + pthread_mutex_destroy(&mq_mutex); + sem_destroy(&dlt_mutex); + // allow the user app to do dlt_init() again. // The flag is unset only to keep almost the same behaviour as before, on EntryNav // This should be removed for other projects (see documentation of dlt_free() @@ -2541,12 +2565,12 @@ void dlt_user_trace_network_segmented_thread(void *unused) while(1) { // Wait until message queue is initialized - pthread_mutex_lock(&mq_mutex); + dlt_lock_mutex(&mq_mutex); if(dlt_user.dlt_segmented_queue_read_handle < 0) { pthread_cond_wait(&mq_init_condition, &mq_mutex); } - pthread_mutex_unlock(&mq_mutex); + dlt_unlock_mutex(&mq_mutex); ssize_t read = mq_receive(dlt_user.dlt_segmented_queue_read_handle, (char *)&data, sizeof(s_segmented_data * ), NULL); @@ -3646,7 +3670,7 @@ int dlt_user_log_send_register_context(DltContextData *log) { DltUserHeader userheader; DltUserControlMsgRegisterContext usercontext; - DltReturnValue ret; + DltReturnValue ret = 0; if (log==0) { @@ -4323,3 +4347,103 @@ void dlt_user_test_corrupt_message_size(int enable,int16_t size) #endif +int dlt_start_threads() +{ + /* Start receiver thread */ + if (pthread_create(&(dlt_receiverthread_handle), + 0, + (void *) &dlt_user_receiverthread_function, + 0)!=0) + { + dlt_log(LOG_CRIT, "Can't create receiver thread!\n"); + return -1; + } + + /* Start the segmented thread */ + if (pthread_create(&(dlt_user.dlt_segmented_nwt_handle), NULL, + (void *)dlt_user_trace_network_segmented_thread, NULL)) + { + dlt_log(LOG_CRIT, "Can't start segmented thread!\n"); + return -1; + } + + return 0; +} + + +void dlt_stop_threads() +{ + int dlt_receiverthread_result = 0; + int dlt_segmented_nwt_result = 0; + if (dlt_receiverthread_handle) + { + /* do not ignore return value */ + dlt_receiverthread_result = pthread_cancel(dlt_receiverthread_handle); + if (dlt_receiverthread_result != 0) + { + snprintf(str,DLT_USER_BUFFER_LENGTH, "ERROR pthread_cancel(dlt_receiverthread_handle): %s\n", strerror(errno)); + dlt_log(LOG_ERR, str); + } + } + + if (dlt_user.dlt_segmented_nwt_handle) + { + dlt_segmented_nwt_result = pthread_cancel(dlt_user.dlt_segmented_nwt_handle); + if (dlt_segmented_nwt_result != 0) + { + snprintf(str,DLT_USER_BUFFER_LENGTH, "ERROR pthread_cancel(dlt_user.dlt_segmented_nwt_handle): %s\n", strerror(errno)); + dlt_log(LOG_ERR, str); + } + } + + /* make sure that the threads really finished working */ + if ((dlt_receiverthread_result==0) && dlt_receiverthread_handle) + { + int joined = pthread_join(dlt_receiverthread_handle, NULL); + if (joined < 0) + { + snprintf(str,DLT_USER_BUFFER_LENGTH, "ERROR pthread_join(dlt_receiverthread_handle, NULL): %s\n", strerror(errno)); + dlt_log(LOG_ERR, str); + } + dlt_receiverthread_handle = 0; /* set to invalid */ + } + + if ((dlt_segmented_nwt_result==0) && dlt_user.dlt_segmented_nwt_handle) + { + int joined = pthread_join(dlt_user.dlt_segmented_nwt_handle, NULL); + if (joined < 0) + { + snprintf(str,DLT_USER_BUFFER_LENGTH, "ERROR pthread_join(dlt_user.dlt_segmented_nwt_handle, NULL): %s\n", strerror(errno)); + dlt_log(LOG_ERR, str); + } + dlt_user.dlt_segmented_nwt_handle = 0; /* set to invalid */ + } +} + + +static void dlt_fork_pre_fork_handler() +{ + dlt_stop_threads(); +} + + +static void dlt_fork_parent_fork_handler() +{ + if (dlt_start_threads() < 0) + { + snprintf(str,DLT_USER_BUFFER_LENGTH,"Logging disabled, failed re-start thread after fork(pid=%i)!\n", getpid()); + dlt_log(LOG_WARNING, str); + /* cleanup is the only thing we can do here */ + dlt_log_free(); + dlt_free(); + } +} + + +static void dlt_fork_child_fork_handler() +{ + /* don't start anything else but cleanup everything and avoid blow-out of buffers*/ + dlt_log_free(); + dlt_free(); + /* the only thing that remains is the atexit-handler */ +} diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 557e403..d4ac21c 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -55,7 +55,12 @@ add_executable(dlt-test-filetransfer ${dlt_test_filetransfer_SRCS}) target_link_libraries(dlt-test-filetransfer dlt) set_target_properties(dlt-test-filetransfer PROPERTIES LINKER_LANGUAGE C) -install(TARGETS dlt-test-multi-process dlt-test-multi-process-client dlt-test-user dlt-test-client dlt-test-stress-user dlt-test-stress-client dlt-test-stress dlt-test-filetransfer +set(dlt_test_fork_handler_SRCS dlt-test-fork-handler.c) +add_executable(dlt-test-fork-handler ${dlt_test_fork_handler_SRCS}) +target_link_libraries(dlt-test-fork-handler dlt) +set_target_properties(dlt-test-fork-handler PROPERTIES LINKER_LANGUAGE C) + +install(TARGETS dlt-test-multi-process dlt-test-multi-process-client dlt-test-user dlt-test-client dlt-test-stress-user dlt-test-stress-client dlt-test-stress dlt-test-filetransfer dlt-test-fork-handler RUNTIME DESTINATION bin COMPONENT base) diff --git a/src/tests/dlt-test-fork-handler.c b/src/tests/dlt-test-fork-handler.c new file mode 100644 index 0000000..d9c65e5 --- /dev/null +++ b/src/tests/dlt-test-fork-handler.c @@ -0,0 +1,47 @@ +/** + * Copyright (C) 2015 Intel Corporation + * + */ + +#include /* for fork() */ + +#include "dlt.h" + +/** + * @brief sample code for using at_fork-handler + */ +int main() +{ + DltContext mainContext; + + DLT_REGISTER_APP("PRNT", "Parent application"); + DLT_REGISTER_CONTEXT(mainContext, "CTXP", "Parent context"); + DLT_LOG(mainContext, DLT_LOG_WARN, DLT_STRING("First message before fork")); + usleep(200000); + + pid_t pid = fork(); + if (pid == 0) /* child process */ + { + /* this message should not be visible */ + /* DLT_LOG(mainContext, DLT_LOG_WARN, DLT_STRING("Child's first message after fork, pid: "), DLT_INT32(getpid())); */ + /* unfortunately, this message does arrive, I assume because it still has (locally) valid data ... */ + + DLT_REGISTER_APP("CHLD", "Child application"); + DLT_REGISTER_CONTEXT(mainContext, "CTXC", "Child context"); + DLT_LOG(mainContext, DLT_LOG_WARN, DLT_STRING("Child's second message after fork, pid: "), DLT_INT32(getpid())); + usleep(400000); + } + else if (pid == -1) /* error in fork */ + { + return -1; + } + else /* parent */ + { + DLT_LOG(mainContext, DLT_LOG_WARN, DLT_STRING("Parent's first message after fork, pid: "), DLT_INT32(getpid())); + usleep(500000); + } + + DLT_UNREGISTER_APP(); + + return 0; +} -- cgit v1.2.1