From: David Goulet Date: Tue, 1 Oct 2013 18:22:22 +0000 (-0400) Subject: Add jul-app ABI/API and handle registration X-Git-Tag: v2.4.0-rc1~91 X-Git-Url: https://git.lttng.org./?a=commitdiff_plain;h=f20baf8e44b5d8a1c90b8b029b876762f6f916a6;p=lttng-tools.git Add jul-app ABI/API and handle registration Registration is done in the jul thread and the created jul-app are attached to existing UST app. At this stage, there is no need for the UST app to be linked to the JUL app but in the future, by adding new features, this will be needed. Enable/Disable one or all events are supported. Also, once a JUL application registers, it's updated with the already enabled events. Signed-off-by: David Goulet --- diff --git a/src/bin/lttng-sessiond/cmd.c b/src/bin/lttng-sessiond/cmd.c index 670992657..8051973de 100644 --- a/src/bin/lttng-sessiond/cmd.c +++ b/src/bin/lttng-sessiond/cmd.c @@ -1022,6 +1022,19 @@ int cmd_disable_event(struct ltt_session *session, int domain, channel_name); break; } + case LTTNG_DOMAIN_JUL: + { + struct ltt_ust_session *usess = session->ust_session; + + assert(usess); + + ret = event_jul_disable(usess, event_name); + if (ret != LTTNG_OK) { + goto error; + } + + break; + } #if 0 case LTTNG_DOMAIN_UST_EXEC_NAME: case LTTNG_DOMAIN_UST_PID: @@ -1112,6 +1125,19 @@ int cmd_disable_event_all(struct ltt_session *session, int domain, DBG3("Disable all UST events in channel %s completed", channel_name); + break; + } + case LTTNG_DOMAIN_JUL: + { + struct ltt_ust_session *usess = session->ust_session; + + assert(usess); + + ret = event_jul_disable_all(usess); + if (ret != LTTNG_OK) { + goto error; + } + break; } #if 0 @@ -1350,6 +1376,46 @@ int cmd_enable_event(struct ltt_session *session, struct lttng_domain *domain, } break; } + case LTTNG_DOMAIN_JUL: + { + struct lttng_event uevent; + struct lttng_domain tmp_dom; + struct ltt_ust_session *usess = session->ust_session; + + assert(usess); + + /* Create the default JUL tracepoint. */ + uevent.type = LTTNG_EVENT_TRACEPOINT; + uevent.loglevel_type = LTTNG_EVENT_LOGLEVEL_ALL; + strncpy(uevent.name, DEFAULT_JUL_EVENT_NAME, sizeof(uevent.name)); + uevent.name[sizeof(uevent.name) - 1] = '\0'; + + /* + * The domain type is changed because we are about to enable the + * default channel and event for the JUL domain that are hardcoded. + * This happens in the UST domain. + */ + memcpy(&tmp_dom, domain, sizeof(tmp_dom)); + tmp_dom.type = LTTNG_DOMAIN_UST; + + ret = cmd_enable_event(session, &tmp_dom, DEFAULT_JUL_CHANNEL_NAME, + &uevent, NULL, wpipe); + if (ret != LTTNG_OK && ret != LTTNG_ERR_UST_EVENT_ENABLED) { + goto error; + } + + /* The wild card * means that everything should be enabled. */ + if (strncmp(event->name, "*", 1) == 0 && strlen(event->name) == 1) { + ret = event_jul_enable_all(usess); + } else { + ret = event_jul_enable(usess, event); + } + if (ret != LTTNG_OK) { + goto error; + } + + break; + } #if 0 case LTTNG_DOMAIN_UST_EXEC_NAME: case LTTNG_DOMAIN_UST_PID: @@ -1520,6 +1586,41 @@ int cmd_enable_event_all(struct ltt_session *session, goto error; } + break; + } + case LTTNG_DOMAIN_JUL: + { + struct lttng_event uevent; + struct lttng_domain tmp_dom; + struct ltt_ust_session *usess = session->ust_session; + + assert(usess); + + /* Create the default JUL tracepoint. */ + uevent.type = LTTNG_EVENT_TRACEPOINT; + uevent.loglevel_type = LTTNG_EVENT_LOGLEVEL_ALL; + strncpy(uevent.name, DEFAULT_JUL_EVENT_NAME, sizeof(uevent.name)); + uevent.name[sizeof(uevent.name) - 1] = '\0'; + + /* + * The domain type is changed because we are about to enable the + * default channel and event for the JUL domain that are hardcoded. + * This happens in the UST domain. + */ + memcpy(&tmp_dom, domain, sizeof(tmp_dom)); + tmp_dom.type = LTTNG_DOMAIN_UST; + + ret = cmd_enable_event(session, &tmp_dom, DEFAULT_JUL_CHANNEL_NAME, + &uevent, NULL, wpipe); + if (ret != LTTNG_OK && ret != LTTNG_ERR_UST_EVENT_ENABLED) { + goto error; + } + + ret = event_jul_enable_all(usess); + if (ret != LTTNG_OK) { + goto error; + } + break; } #if 0 diff --git a/src/bin/lttng-sessiond/event.c b/src/bin/lttng-sessiond/event.c index 9a8ed63d1..77818e587 100644 --- a/src/bin/lttng-sessiond/event.c +++ b/src/bin/lttng-sessiond/event.c @@ -607,3 +607,180 @@ error: rcu_read_unlock(); return ret; } + +/* + * Enable all JUL event for a given UST session. + * + * Return LTTNG_OK on success or else a LTTNG_ERR* code. + */ +int event_jul_enable_all(struct ltt_ust_session *usess) +{ + int ret; + struct jul_event *jevent; + struct lttng_event event; + struct lttng_ht_iter iter; + + assert(usess); + + DBG("Event JUL enabling ALL events for session %" PRIu64, usess->id); + + /* Create the * wildcard event name for the Java agent. */ + memset(event.name, 0, sizeof(event.name)); + strncpy(event.name, "*", sizeof(event.name)); + event.name[sizeof(event.name) - 1] = '\0'; + + /* Enable event on JUL application through TCP socket. */ + ret = event_jul_enable(usess, &event); + if (ret != LTTNG_OK) { + goto error; + } + + /* Flag every event that they are now enabled. */ + rcu_read_lock(); + cds_lfht_for_each_entry(usess->domain_jul.events->ht, &iter.iter, jevent, + node.node) { + jevent->enabled = 1; + } + rcu_read_unlock(); + + ret = LTTNG_OK; + +error: + return ret; +} + +/* + * Enable a single JUL event for a given UST session. + * + * Return LTTNG_OK on success or else a LTTNG_ERR* code. + */ +int event_jul_enable(struct ltt_ust_session *usess, struct lttng_event *event) +{ + int ret, created = 0; + struct jul_event *jevent; + + assert(usess); + assert(event); + + DBG("Event JUL enabling %s for session %" PRIu64, event->name, usess->id); + + jevent = jul_find_by_name(event->name, &usess->domain_jul); + if (!jevent) { + jevent = jul_create_event(event->name); + if (!jevent) { + ret = LTTNG_ERR_NOMEM; + goto error; + } + created = 1; + } + + /* Already enabled? */ + if (jevent->enabled) { + goto end; + } + + ret = jul_enable_event(jevent); + if (ret != LTTNG_OK) { + goto error; + } + + /* If the event was created prior to the enable, add it to the domain. */ + if (created) { + jul_add_event(jevent, &usess->domain_jul); + } + +end: + return LTTNG_OK; + +error: + if (created) { + jul_destroy_event(jevent); + } + return ret; +} + +/* + * Disable a single JUL event for a given UST session. + * + * Return LTTNG_OK on success or else a LTTNG_ERR* code. + */ +int event_jul_disable(struct ltt_ust_session *usess, char *event_name) +{ + int ret; + struct jul_event *jevent; + + assert(usess); + assert(event_name); + + DBG("Event JUL disabling %s for session %" PRIu64, event_name, usess->id); + + jevent = jul_find_by_name(event_name, &usess->domain_jul); + if (!jevent) { + ret = LTTNG_ERR_UST_EVENT_NOT_FOUND; + goto error; + } + + /* Already disabled? */ + if (!jevent->enabled) { + goto end; + } + + ret = jul_disable_event(jevent); + if (ret != LTTNG_OK) { + goto error; + } + +end: + return LTTNG_OK; + +error: + return ret; +} +/* + * Disable all JUL event for a given UST session. + * + * Return LTTNG_OK on success or else a LTTNG_ERR* code. + */ +int event_jul_disable_all(struct ltt_ust_session *usess) +{ + int ret, do_disable = 0; + struct jul_event *jevent; + struct lttng_ht_iter iter; + + assert(usess); + + /* Enable event on JUL application through TCP socket. */ + ret = event_jul_disable(usess, "*"); + if (ret != LTTNG_OK) { + if (ret == LTTNG_ERR_UST_EVENT_NOT_FOUND) { + /* + * This means that no enable all was done before but still a user + * could want to disable everything even though the * wild card + * event does not exists. + */ + do_disable = 1; + } else { + goto error; + } + } + + /* Flag every event that they are now enabled. */ + rcu_read_lock(); + cds_lfht_for_each_entry(usess->domain_jul.events->ht, &iter.iter, jevent, + node.node) { + if (jevent->enabled && do_disable) { + ret = event_jul_disable(usess, jevent->name); + if (ret != LTTNG_OK) { + rcu_read_unlock(); + goto error; + } + } + jevent->enabled = 0; + } + rcu_read_unlock(); + + ret = LTTNG_OK; + +error: + return ret; +} diff --git a/src/bin/lttng-sessiond/event.h b/src/bin/lttng-sessiond/event.h index 819111599..1b92a6a7f 100644 --- a/src/bin/lttng-sessiond/event.h +++ b/src/bin/lttng-sessiond/event.h @@ -45,4 +45,10 @@ int event_ust_enable_all_tracepoints(struct ltt_ust_session *usess, int event_ust_disable_all_tracepoints(struct ltt_ust_session *usess, struct ltt_ust_channel *uchan); +int event_jul_enable(struct ltt_ust_session *usess, struct lttng_event *event); +int event_jul_enable_all(struct ltt_ust_session *usess); + +int event_jul_disable(struct ltt_ust_session *usess, char *event_name); +int event_jul_disable_all(struct ltt_ust_session *usess); + #endif /* _LTT_EVENT_H */ diff --git a/src/bin/lttng-sessiond/jul-thread.c b/src/bin/lttng-sessiond/jul-thread.c index 9bc82eac7..972186059 100644 --- a/src/bin/lttng-sessiond/jul-thread.c +++ b/src/bin/lttng-sessiond/jul-thread.c @@ -26,6 +26,8 @@ #include "fd-limit.h" #include "jul-thread.h" #include "lttng-sessiond.h" +#include "session.h" +#include "utils.h" /* * Note that there is not port here. It's set after this URI is parsed so we @@ -34,6 +36,79 @@ */ static const char *default_reg_uri = "tcp://localhost"; +/* + * Update JUL application using the given socket. This is done just after + * registration was successful. + * + * This is a quite heavy call in terms of locking since the session list lock + * AND session lock are acquired. + */ +static void update_jul_app(int sock) +{ + struct ltt_session *session, *stmp; + struct ltt_session_list *list; + + list = session_get_list(); + assert(list); + + session_lock_list(); + cds_list_for_each_entry_safe(session, stmp, &list->head, list) { + session_lock(session); + if (session->ust_session) { + jul_update(&session->ust_session->domain_jul, sock); + } + session_unlock(session); + } + session_unlock_list(); +} + +/* + * Destroy a JUL application by socket. + */ +static void destroy_jul_app(int sock) +{ + struct jul_app *app; + + assert(sock >= 0); + + /* + * Not finding an application is a very important error that should NEVER + * happen. The hash table deletion is ONLY done through this call even on + * thread cleanup. + */ + rcu_read_lock(); + app = jul_find_app_by_sock(sock); + assert(app); + rcu_read_unlock(); + + /* RCU read side lock is taken in this function call. */ + jul_delete_app(app); + + /* The application is freed in a RCU call but the socket is closed here. */ + jul_destroy_app(app); +} + +/* + * Cleanup remaining JUL apps in the hash table. This should only be called in + * the exit path of the thread. + */ +static void clean_jul_apps_ht(void) +{ + struct lttng_ht_node_ulong *node; + struct lttng_ht_iter iter; + + DBG3("[jul-thread] Cleaning JUL apps ht"); + + rcu_read_lock(); + cds_lfht_for_each_entry(jul_apps_ht_by_sock->ht, &iter.iter, node, node) { + struct jul_app *app; + + app = caa_container_of(node, struct jul_app, node); + destroy_jul_app(app->sock->fd); + } + rcu_read_unlock(); +} + /* * Create and init socket from uri. */ @@ -100,6 +175,71 @@ static void destroy_tcp_socket(struct lttcomm_sock *sock) lttcomm_destroy_sock(sock); } +/* + * Handle a new JUL registration using the reg socket. After that, a new JUL + * application is added to the global hash table and attach to an UST app + * object. + * + * Return the new FD created upon accept() on success or else a negative errno + * value. + */ +static int handle_registration(struct lttcomm_sock *reg_sock) +{ + int ret; + pid_t pid; + ssize_t size; + struct jul_app *app; + struct jul_register_msg msg; + struct lttcomm_sock *new_sock; + + assert(reg_sock); + + new_sock = reg_sock->ops->accept(reg_sock); + if (!new_sock) { + ret = -ENOTCONN; + goto error; + } + + size = new_sock->ops->recvmsg(new_sock, &msg, sizeof(msg), 0); + if (size < sizeof(msg)) { + ret = -errno; + goto error_socket; + } + pid = be32toh(msg.pid); + + DBG2("[jul-thread] New registration for pid %d on socket %d", pid, + new_sock->fd); + + app = jul_create_app(pid, new_sock); + if (!app) { + ret = -ENOMEM; + goto error_socket; + } + + /* + * Add before assigning the socket value to the UST app so it can be found + * concurrently. + */ + jul_add_app(app); + + /* + * Attach JUL application to a UST app object if one exists. + * + * FIXME: This implies that the UST app object exists and created before + * JUL registration. Must confirm or else JUL app will leak until socket is + * closed by the application. + */ + jul_attach_app(app); + + return new_sock->fd; + +error_socket: + new_sock->ops->close(new_sock); + lttcomm_destroy_sock(new_sock); +error: + return ret; +} + /* * This thread manage application notify communication. */ @@ -115,6 +255,9 @@ void *jul_thread_manage_registration(void *data) rcu_register_thread(); rcu_thread_online(); + /* JUL initialization call MUST be called before starting the thread. */ + assert(jul_apps_ht_by_sock); + /* Create pollset with size 2, quit pipe and socket. */ ret = sessiond_set_thread_pollset(&events, 2); if (ret < 0) { @@ -150,6 +293,7 @@ restart: goto error; } nb_fd = ret; + DBG3("[jul-thread] %d fd ready", nb_fd); for (i = 0; i < nb_fd; i++) { /* Fetch once the poll data */ @@ -173,12 +317,34 @@ restart: goto error; } - /* FIXME: Nullify the JUL socket for the associated ust app. */ - } else if (revents & (LPOLLIN | LPOLLPRI)) { /* - * FIXME: Handle JUL registration which must link an UST-app - * and this JUL socket. + * FIXME: Should we try to invalidate the JUL socket in the + * associated ust app. */ + destroy_jul_app(pollfd); + } else if (revents & (LPOLLIN)) { + int new_fd; + + /* Pollin event of JUL app socket should NEVER happen. */ + assert(pollfd == reg_sock->fd); + + new_fd = handle_registration(reg_sock); + if (new_fd < 0) { + WARN("[jul-thread] JUL registration failed. Ignoring."); + /* Somehow the communication failed. Just continue. */ + continue; + } + + /* Only add poll error event to only detect shutdown. */ + ret = lttng_poll_add(&events, new_fd, + LPOLLERR | LPOLLHUP | LPOLLRDHUP); + if (ret < 0) { + destroy_jul_app(new_fd); + continue; + } + + /* Update newly registered app. */ + update_jul_app(new_fd); } else { ERR("Unknown poll events %u for sock %d", revents, pollfd); continue; @@ -187,6 +353,8 @@ restart: } exit: + /* Whatever happens, try to delete it and exit. */ + (void) lttng_poll_del(&events, reg_sock->fd); error: destroy_tcp_socket(reg_sock); error_tcp_socket: @@ -194,6 +362,11 @@ error_tcp_socket: error_poll_create: DBG("[jul-thread] is cleaning up and stopping."); + if (jul_apps_ht_by_sock) { + clean_jul_apps_ht(); + lttng_ht_destroy(jul_apps_ht_by_sock); + } + rcu_thread_offline(); rcu_unregister_thread(); return NULL; diff --git a/src/bin/lttng-sessiond/jul.c b/src/bin/lttng-sessiond/jul.c index a38738c3b..87ab654a3 100644 --- a/src/bin/lttng-sessiond/jul.c +++ b/src/bin/lttng-sessiond/jul.c @@ -17,10 +17,13 @@ #define _GNU_SOURCE #include +#include #include +#include #include "jul.h" +#include "ust-app.h" #include "utils.h" /* @@ -36,6 +39,511 @@ static void destroy_event_jul_rcu(struct rcu_head *head) free(event); } +/* + * URCU intermediate call to complete destroy a JUL event. + */ +static void destroy_app_jul_rcu(struct rcu_head *head) +{ + struct lttng_ht_node_ulong *node = + caa_container_of(head, struct lttng_ht_node_ulong, head); + struct jul_app *app = + caa_container_of(node, struct jul_app, node); + + free(app); +} + +/* + * Communication with Java agent call. Send the message header to the given + * socket all in big endian. + * + * Return 0 on success or else a negative errno message of sendmsg() op. + */ +static int send_header(struct lttcomm_sock *sock, uint64_t data_size, + uint32_t cmd, uint32_t cmd_version) +{ + int ret; + ssize_t size; + struct lttcomm_jul_hdr msg; + + assert(sock); + + msg.data_size = htobe64(data_size); + msg.cmd = htobe32(cmd); + msg.cmd_version = htobe32(cmd_version); + + size = sock->ops->sendmsg(sock, &msg, sizeof(msg), 0); + if (size < sizeof(msg)) { + ret = -errno; + goto error; + } + ret = 0; + +error: + return ret; +} + +/* + * Communication call with the Java agent. Send the payload to the given + * socket. The header MUST be sent prior to this call. + * + * Return 0 on success or else a negative errno value of sendmsg() op. + */ +static int send_payload(struct lttcomm_sock *sock, void *data, + size_t size) +{ + int ret; + ssize_t len; + + assert(sock); + assert(data); + + len = sock->ops->sendmsg(sock, data, size, 0); + if (len < size) { + ret = -errno; + goto error; + } + ret = 0; + +error: + return ret; +} + +/* + * Communication call with the Java agent. Receive reply from the agent using + * the given socket. + * + * Return 0 on success or else a negative errno value from recvmsg() op. + */ +static int recv_reply(struct lttcomm_sock *sock, void *buf, size_t size) +{ + int ret; + ssize_t len; + + assert(sock); + assert(buf); + + len = sock->ops->recvmsg(sock, buf, size, 0); + if (len < size) { + ret = -errno; + goto error; + } + ret = 0; + +error: + return ret; +} + +/* + * Internal enable JUL event call on a JUL application. This function + * communicates with the Java agent to enable a given event (Logger name). + * + * Return LTTNG_OK on success or else a LTTNG_ERR* code. + */ +static int enable_event(struct jul_app *app, struct jul_event *event) +{ + int ret; + uint64_t data_size; + struct lttcomm_jul_enable msg; + struct lttcomm_jul_generic_reply reply; + + assert(app); + assert(app->sock); + assert(event); + + DBG2("JUL enabling event %s for app pid: %d and socket %d", event->name, + app->pid, app->sock->fd); + + data_size = sizeof(msg); + + ret = send_header(app->sock, data_size, JUL_CMD_ENABLE, 0); + if (ret < 0) { + goto error_io; + } + + strncpy(msg.name, event->name, sizeof(msg.name)); + ret = send_payload(app->sock, &msg, sizeof(msg)); + if (ret < 0) { + goto error_io; + } + + ret = recv_reply(app->sock, &reply, sizeof(reply)); + if (ret < 0) { + goto error_io; + } + + switch (be32toh(reply.ret_code)) { + case JUL_RET_CODE_SUCCESS: + break; + case JUL_RET_CODE_UNKNOWN_NAME: + ret = LTTNG_ERR_UST_EVENT_NOT_FOUND; + goto error; + default: + ERR("Java agent returned an unknown code: %" PRIu32, + be32toh(reply.ret_code)); + ret = LTTNG_ERR_FATAL; + goto error; + } + + return LTTNG_OK; + +error_io: + ret = LTTNG_ERR_UST_ENABLE_FAIL; +error: + return ret; +} + +/* + * Internal disable JUL event call on a JUL application. This function + * communicates with the Java agent to disable a given event (Logger name). + * + * Return LTTNG_OK on success or else a LTTNG_ERR* code. + */ +static int disable_event(struct jul_app *app, struct jul_event *event) +{ + int ret; + uint64_t data_size; + struct lttcomm_jul_disable msg; + struct lttcomm_jul_generic_reply reply; + + assert(app); + assert(app->sock); + assert(event); + + DBG2("JUL disabling event %s for app pid: %d and socket %d", event->name, + app->pid, app->sock->fd); + + data_size = sizeof(msg); + + ret = send_header(app->sock, data_size, JUL_CMD_DISABLE, 0); + if (ret < 0) { + goto error_io; + } + + strncpy(msg.name, event->name, sizeof(msg.name)); + ret = send_payload(app->sock, &msg, sizeof(msg)); + if (ret < 0) { + goto error_io; + } + + ret = recv_reply(app->sock, &reply, sizeof(reply)); + if (ret < 0) { + goto error_io; + } + + switch (be32toh(reply.ret_code)) { + case JUL_RET_CODE_SUCCESS: + break; + case JUL_RET_CODE_UNKNOWN_NAME: + ret = LTTNG_ERR_UST_EVENT_NOT_FOUND; + goto error; + default: + ERR("Java agent returned an unknown code: %" PRIu32, + be32toh(reply.ret_code)); + ret = LTTNG_ERR_FATAL; + goto error; + } + + return LTTNG_OK; + +error_io: + ret = LTTNG_ERR_UST_DISABLE_FAIL; +error: + return ret; +} + +/* + * Enable JUL event on every JUL applications registered with the session + * daemon. + * + * Return LTTNG_OK on success or else a LTTNG_ERR* code. + */ +int jul_enable_event(struct jul_event *event) +{ + int ret; + struct jul_app *app; + struct lttng_ht_iter iter; + + assert(event); + + rcu_read_lock(); + + cds_lfht_for_each_entry(jul_apps_ht_by_sock->ht, &iter.iter, app, + node.node) { + /* Enable event on JUL application through TCP socket. */ + ret = enable_event(app, event); + if (ret != LTTNG_OK) { + goto error; + } + event->enabled = 1; + } + + ret = LTTNG_OK; + +error: + rcu_read_unlock(); + return ret; +} + +/* + * Disable JUL event on every JUL applications registered with the session + * daemon. + * + * Return LTTNG_OK on success or else a LTTNG_ERR* code. + */ +int jul_disable_event(struct jul_event *event) +{ + int ret; + struct jul_app *app; + struct lttng_ht_iter iter; + + assert(event); + + rcu_read_lock(); + + cds_lfht_for_each_entry(jul_apps_ht_by_sock->ht, &iter.iter, app, + node.node) { + /* Enable event on JUL application through TCP socket. */ + ret = disable_event(app, event); + if (ret != LTTNG_OK) { + goto error; + } + event->enabled = 0; + } + + ret = LTTNG_OK; + +error: + rcu_read_unlock(); + return ret; +} + +/* + * Ask every java agent for the list of possible event (logger name). Events is + * allocated with the events of every JUL application. + * + * Return the number of events or else a negative value. + */ +int jul_list_events(struct lttng_event **events) +{ + int ret; + size_t nbmem, count = 0; + struct jul_app *app; + struct lttng_event *tmp_events; + struct lttng_ht_iter iter; + + assert(events); + + nbmem = UST_APP_EVENT_LIST_SIZE; + tmp_events = zmalloc(nbmem * sizeof(*tmp_events)); + if (!tmp_events) { + PERROR("zmalloc jul list events"); + ret = -ENOMEM; + goto error; + } + + rcu_read_lock(); + cds_lfht_for_each_entry(jul_apps_ht_by_sock->ht, &iter.iter, app, + node.node) { + ssize_t nb_ev; + struct lttng_event *jul_events; + + nb_ev = list_events(app, &jul_events); + if (nb_ev < 0) { + ret = nb_ev; + rcu_read_unlock(); + goto error; + } + + if (count >= nbmem) { + /* In case the realloc fails, we free the memory */ + void *ptr; + + DBG2("Reallocating JUL event list from %zu to %zu entries", nbmem, + 2 * nbmem); + nbmem *= 2; + ptr = realloc(tmp_events, nbmem * sizeof(*tmp_events)); + if (!ptr) { + PERROR("realloc JUL events"); + free(tmp_events); + ret = -ENOMEM; + rcu_read_unlock(); + goto error; + } + tmp_events = ptr; + } + memcpy(tmp_events + (count * sizeof(*tmp_events)), jul_events, + nb_ev * sizeof(*tmp_events)); + free(jul_events); + count += nb_ev; + } + rcu_read_unlock(); + + ret = count; + *events = tmp_events; + +error: + return ret; +} + +/* + * Create a JUL app object using the given PID. + * + * Return newly allocated object or else NULL on error. + */ +struct jul_app *jul_create_app(pid_t pid, struct lttcomm_sock *sock) +{ + struct jul_app *app; + + assert(sock); + + app = zmalloc(sizeof(*app)); + if (!app) { + PERROR("zmalloc JUL create"); + goto error; + } + + app->pid = pid; + app->sock = sock; + /* Flag it invalid until assignation. */ + app->ust_app_sock = -1; + lttng_ht_node_init_ulong(&app->node, (unsigned long) app->sock->fd); + +error: + return app; +} + +/* + * Lookup JUL app by socket in the global hash table. + * + * RCU read side lock MUST be acquired. + * + * Return object if found else NULL. + */ +struct jul_app *jul_find_app_by_sock(int sock) +{ + struct lttng_ht_node_ulong *node; + struct lttng_ht_iter iter; + struct jul_app *app; + + assert(sock >= 0); + + lttng_ht_lookup(jul_apps_ht_by_sock, (void *)((unsigned long) sock), &iter); + node = lttng_ht_iter_get_node_ulong(&iter); + if (node == NULL) { + goto error; + } + app = caa_container_of(node, struct jul_app, node); + + DBG3("JUL app pid %d found by sock %d.", app->pid, sock); + return app; + +error: + DBG3("JUL app NOT found by sock %d.", sock); + return NULL; +} + +/* + * Add JUL application object to a given hash table. + */ +void jul_add_app(struct jul_app *app) +{ + assert(app); + + DBG3("JUL adding app sock: %d and pid: %d to ht", app->sock->fd, app->pid); + + rcu_read_lock(); + lttng_ht_add_unique_ulong(jul_apps_ht_by_sock, &app->node); + rcu_read_unlock(); +} + +/* + * Attach a given JUL application to an UST app object. This is done by copying + * the socket fd value into the ust app obj. atomically. + */ +void jul_attach_app(struct jul_app *japp) +{ + struct ust_app *uapp; + + assert(japp); + + rcu_read_lock(); + uapp = ust_app_find_by_pid(japp->pid); + if (!uapp) { + goto end; + } + + uatomic_set(&uapp->jul_app_sock, japp->sock->fd); + + DBG3("JUL app pid: %d, sock: %d attached to UST app.", japp->pid, + japp->sock->fd); + +end: + rcu_read_unlock(); + return; +} + +/* + * Remove JUL app. reference from an UST app object and set it to NULL. + */ +void jul_detach_app(struct jul_app *japp) +{ + struct ust_app *uapp; + + assert(japp); + + rcu_read_lock(); + + if (japp->ust_app_sock < 0) { + goto end; + } + + uapp = ust_app_find_by_sock(japp->ust_app_sock); + if (!uapp) { + goto end; + } + + uapp->jul_app_sock = -1; + +end: + rcu_read_unlock(); + return; +} + +/* + * Delete JUL application from the global hash table. + */ +void jul_delete_app(struct jul_app *app) +{ + int ret; + struct lttng_ht_iter iter; + + assert(app); + + DBG3("JUL deleting app pid: %d and sock: %d", app->pid, app->sock->fd); + + iter.iter.node = &app->node.node; + rcu_read_lock(); + ret = lttng_ht_del(jul_apps_ht_by_sock, &iter); + rcu_read_unlock(); + assert(!ret); +} + +/* + * Destroy a JUL application object by detaching it from its corresponding UST + * app if one, closing the socket and freeing the memory. + */ +void jul_destroy_app(struct jul_app *app) +{ + assert(app); + + if (app->sock) { + app->sock->ops->close(app->sock); + lttcomm_destroy_sock(app->sock); + } + + call_rcu(&app->node.head, destroy_app_jul_rcu); +} + /* * Initialize an already allocated JUL domain object. * @@ -79,6 +587,7 @@ struct jul_event *jul_create_event(const char *name) if (name) { strncpy(event->name, name, sizeof(event->name)); event->name[sizeof(event->name) - 1] = '\0'; + lttng_ht_node_init_str(&event->node, event->name); } error: @@ -96,7 +605,9 @@ void jul_add_event(struct jul_event *event, struct jul_domain *dom) DBG3("JUL adding event %s to domain", event->name); + rcu_read_lock(); lttng_ht_add_unique_str(dom->events, &event->node); + rcu_read_unlock(); } /* @@ -191,5 +702,61 @@ void jul_destroy_domain(struct jul_domain *dom) } rcu_read_unlock(); - ht_cleanup_push(dom->events); + lttng_ht_destroy(dom->events); +} + +/* + * Initialize JUL subsystem. + */ +int jul_init(void) +{ + jul_apps_ht_by_sock = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); + if (!jul_apps_ht_by_sock) { + return -1; + } + + return 0; +} + +/* + * Update a JUL application (given socket) using the given domain. + * + * Note that this function is most likely to be used with a tracing session + * thus the caller should make sure to hold the appropriate lock(s). + */ +void jul_update(struct jul_domain *domain, int sock) +{ + int ret; + struct jul_app *app; + struct jul_event *event; + struct lttng_ht_iter iter; + + assert(domain); + assert(sock >= 0); + + DBG("JUL updating app socket %d", sock); + + rcu_read_lock(); + cds_lfht_for_each_entry(domain->events->ht, &iter.iter, event, node.node) { + /* Skip event if disabled. */ + if (!event->enabled) { + continue; + } + + app = jul_find_app_by_sock(sock); + /* + * We are in the registration path thus if the application is gone, + * there is a serious code flow error. + */ + assert(app); + + ret = enable_event(app, event); + if (ret != LTTNG_OK) { + DBG2("JUL update unable to enable event %s on app pid: %d sock %d", + event->name, app->pid, app->sock->fd); + /* Let's try the others here and don't assume the app is dead. */ + continue; + } + } + rcu_read_unlock(); } diff --git a/src/bin/lttng-sessiond/jul.h b/src/bin/lttng-sessiond/jul.h index 1f7f6cdcd..f7d3c48f8 100644 --- a/src/bin/lttng-sessiond/jul.h +++ b/src/bin/lttng-sessiond/jul.h @@ -18,9 +18,53 @@ #ifndef _JUL_H #define _JUL_H +#define _GNU_SOURCE +#include + #include #include +/* + * Hash table that contains the JUL app created upon registration indexed by + * socket. + */ +struct lttng_ht *jul_apps_ht_by_sock; + +/* + * Registration message payload from a JUL application. The PID is used to find + * back the corresponding UST app object so both socket can be linked. + */ +struct jul_register_msg { + uint32_t pid; +}; + +/* + * JUL application object created after a successful registration. This object + * is kept inside an UST app. + */ +struct jul_app { + /* + * PID sent during registration of a JUL application. + */ + pid_t pid; + + /* + * JUL TCP socket that was created upon registration. + */ + struct lttcomm_sock *sock; + + /* + * Associated UST app. socket. To get a reference to the ust application + * object corresponding to that socket, a lookup MUST be done each time + * since there is important synchronization issue for the lockless hash + * table shared accross multiple threads. + */ + int ust_app_sock; + + /* Initialized with the JUL sock value. */ + struct lttng_ht_node_ulong node; +}; + /* * Java Util Logging event representation. */ @@ -53,12 +97,32 @@ struct jul_domain { struct lttng_ht *events; }; +/* Initialize JUL domain subsystem. */ +int jul_init(void); + +/* Initialize an already allocated JUL domain. */ int jul_init_domain(struct jul_domain *dom); +void jul_destroy_domain(struct jul_domain *dom); + +/* JUL event API. */ struct jul_event *jul_create_event(const char *name); void jul_add_event(struct jul_event *event, struct jul_domain *dom); struct jul_event *jul_find_by_name(const char *name, struct jul_domain *dom); void jul_delete_event(struct jul_event *event, struct jul_domain *dom); void jul_destroy_event(struct jul_event *event); -void jul_destroy_domain(struct jul_domain *dom); + +/* JUL app API. */ +struct jul_app *jul_create_app(pid_t pid, struct lttcomm_sock *sock); +void jul_add_app(struct jul_app *app); +void jul_delete_app(struct jul_app *app); +struct jul_app *jul_find_app_by_sock(int sock); +void jul_attach_app(struct jul_app *japp); +void jul_detach_app(struct jul_app *app); +void jul_destroy_app(struct jul_app *app); + +/* JUL action API */ +int jul_enable_event(struct jul_event *event); +int jul_disable_event(struct jul_event *event); +void jul_update(struct jul_domain *domain, int sock); #endif /* _JUL_H */ diff --git a/src/bin/lttng-sessiond/main.c b/src/bin/lttng-sessiond/main.c index 6d6140b1f..05646279a 100644 --- a/src/bin/lttng-sessiond/main.c +++ b/src/bin/lttng-sessiond/main.c @@ -2415,6 +2415,7 @@ static int copy_session_consumer(int domain, struct ltt_session *session) consumer = session->kernel_session->consumer; dir_name = DEFAULT_KERNEL_TRACE_DIR; break; + case LTTNG_DOMAIN_JUL: case LTTNG_DOMAIN_UST: DBG3("Copying tracing session consumer output in UST session"); if (session->ust_session->consumer) { @@ -2458,6 +2459,7 @@ static int create_ust_session(struct ltt_session *session, assert(session->consumer); switch (domain->type) { + case LTTNG_DOMAIN_JUL: case LTTNG_DOMAIN_UST: break; default: @@ -2750,10 +2752,6 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock, break; case LTTNG_DOMAIN_JUL: - { - ret = LTTNG_ERR_UNKNOWN_DOMAIN; - goto error; - } case LTTNG_DOMAIN_UST: { if (!ust_app_supported()) { @@ -4628,6 +4626,12 @@ int main(int argc, char **argv) */ ust_app_ht_alloc(); + /* Initialize JUL domain subsystem. */ + if ((ret = jul_init()) < 0) { + /* ENOMEM at this point. */ + goto error; + } + /* After this point, we can safely call cleanup() with "goto exit" */ /* diff --git a/src/bin/lttng-sessiond/ust-app.c b/src/bin/lttng-sessiond/ust-app.c index 5464880fc..11b588f77 100644 --- a/src/bin/lttng-sessiond/ust-app.c +++ b/src/bin/lttng-sessiond/ust-app.c @@ -957,8 +957,7 @@ error: * Find an ust_app using the sock and return it. RCU read side lock must be * held before calling this helper function. */ -static -struct ust_app *find_app_by_sock(int sock) +struct ust_app *ust_app_find_by_sock(int sock) { struct lttng_ht_node_ulong *node; struct lttng_ht_iter iter; @@ -4152,7 +4151,7 @@ void ust_app_global_update(struct ltt_ust_session *usess, int sock) rcu_read_lock(); - app = find_app_by_sock(sock); + app = ust_app_find_by_sock(sock); if (app == NULL) { /* * Application can be unregistered before so this is possible hence diff --git a/src/bin/lttng-sessiond/ust-app.h b/src/bin/lttng-sessiond/ust-app.h index edec8d496..249d568d1 100644 --- a/src/bin/lttng-sessiond/ust-app.h +++ b/src/bin/lttng-sessiond/ust-app.h @@ -21,6 +21,8 @@ #include #include + +#include "jul.h" #include "trace-ust.h" #include "ust-registry.h" @@ -259,6 +261,13 @@ struct ust_app { * Hash table containing ust_app_channel indexed by channel objd. */ struct lttng_ht *ust_objd; + /* + * If this application is of the JUL domain and this is non negative then a + * lookup MUST be done to acquire a read side reference to the + * corresponding JUL app object. If the lookup fails, this should be set to + * a negative value indicating that the JUL application is gone. + */ + int jul_app_sock; }; #ifdef HAVE_LIBLTTNG_UST_CTL @@ -320,6 +329,7 @@ void ust_app_destroy(struct ust_app *app); int ust_app_snapshot_record(struct ltt_ust_session *usess, struct snapshot_output *output, int wait, unsigned int nb_streams); unsigned int ust_app_get_nb_stream(struct ltt_ust_session *usess); +struct ust_app *ust_app_find_by_sock(int sock); static inline int ust_app_supported(void) @@ -540,6 +550,11 @@ int ust_app_supported(void) { return 0; } +static inline +struct ust_app *ust_app_find_by_sock(int sock) +{ + return NULL; +} #endif /* HAVE_LIBLTTNG_UST_CTL */ diff --git a/src/common/defaults.h b/src/common/defaults.h index 0010e0616..821128eb4 100644 --- a/src/common/defaults.h +++ b/src/common/defaults.h @@ -133,7 +133,9 @@ /* Default channel attributes */ #define DEFAULT_CHANNEL_NAME "channel0" /* Default JUL domain channel name. */ -#define DEFAULT_JUL_CHANNEL_NAME "jul_channel" +#define DEFAULT_JUL_CHANNEL_NAME "lttng_jul_channel" +/* Default JUL tracepoint name. This is a wildcard for the JUL domain. */ +#define DEFAULT_JUL_EVENT_NAME "lttng_jul*" /* JUL default channel name. */ #define DEFAULT_CHANNEL_OVERWRITE 0 #define DEFAULT_CHANNEL_TRACEFILE_SIZE 0 diff --git a/src/common/sessiond-comm/Makefile.am b/src/common/sessiond-comm/Makefile.am index 72375c0e9..c72c87fbc 100644 --- a/src/common/sessiond-comm/Makefile.am +++ b/src/common/sessiond-comm/Makefile.am @@ -5,5 +5,5 @@ noinst_LTLIBRARIES = libsessiond-comm.la libsessiond_comm_la_SOURCES = sessiond-comm.c sessiond-comm.h \ unix.c unix.h inet.c inet.h inet6.c inet6.h \ - relayd.h + relayd.h jul.h libsessiond_comm_la_LIBADD = -lrt diff --git a/src/common/sessiond-comm/jul.h b/src/common/sessiond-comm/jul.h new file mode 100644 index 000000000..fa14dbf88 --- /dev/null +++ b/src/common/sessiond-comm/jul.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2013 - David Goulet + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License, version 2 only, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _JUL_COMM +#define _JUL_COMM + +#define _GNU_SOURCE +#include + +#include + +/* + * Command value pass in the header. + */ +enum lttcomm_jul_command { + JUL_CMD_LIST = 1, + JUL_CMD_ENABLE = 2, + JUL_CMD_DISABLE = 3, +}; + +/* + * Return code from the Java agent. + */ +enum lttcomm_jul_ret_code { + JUL_RET_CODE_SUCCESS = 1, + JUL_RET_CODE_INVALID = 2, + JUL_RET_CODE_UNKNOWN_NAME = 3, +}; + +/* + * JUL application communication header. + */ +struct lttcomm_jul_hdr { + uint64_t data_size; /* data size following this header */ + uint32_t cmd; /* Enum of JUL command. */ + uint32_t cmd_version; /* command version */ +} LTTNG_PACKED; + +/* + * Enable event command payload. + */ +struct lttcomm_jul_enable { + char name[LTTNG_SYMBOL_NAME_LEN]; +} LTTNG_PACKED; + +/* + * Disable event command payload. + */ +struct lttcomm_jul_disable { + char name[LTTNG_SYMBOL_NAME_LEN]; +} LTTNG_PACKED; + +/* + * Generic reply coming from the Java Agent. + */ +struct lttcomm_jul_generic_reply { + uint32_t ret_code; +} LTTNG_PACKED; + +/* + * List command reply header. + */ +struct lttcomm_jul_list_reply_hdr { + uint32_t ret_code; + uint32_t data_size; +} LTTNG_PACKED; + +/* + * List command reply payload coming from the Java Agent. + */ +struct lttcomm_jul_list_reply { + uint32_t nb_event; + /* List of event name each of them ending by a NULL byte. */ + char payload[]; +} LTTNG_PACKED; + +#endif /* _JUL_COMM */