From 0e1155633b00241b8b1e5a9bb683cf91b28f5eeb Mon Sep 17 00:00:00 2001 From: David Goulet Date: Tue, 14 Oct 2014 11:15:22 -0400 Subject: [PATCH] Add Python agent support Support the new Python agent shipped in liblttng-ust. This adds the -p, --python option to the list and enable/disable-event command to control the domain exactly like JUL and LOG4J. The agent support is for the Python "logging" module. Signed-off-by: David Goulet --- configure.ac | 1 + doc/man/lttng.1 | 24 +- include/lttng/domain.h | 1 + include/lttng/event.h | 13 + src/bin/lttng-sessiond/agent.c | 2 + src/bin/lttng-sessiond/cmd.c | 17 +- src/bin/lttng-sessiond/event.c | 23 +- src/bin/lttng-sessiond/main.c | 5 + src/bin/lttng-sessiond/save.c | 3 +- src/bin/lttng/commands/disable_events.c | 5 + src/bin/lttng/commands/enable_events.c | 65 +- src/bin/lttng/commands/list.c | 23 +- src/bin/lttng/utils.c | 4 + src/common/config/config.c | 11 + src/common/config/session.xsd | 1 + src/common/defaults.h | 6 +- src/common/mi-lttng.c | 26 + src/common/mi-lttng.h | 8 + src/common/mi_lttng.xsd | 7 + src/lib/lttng-ctl/lttng-ctl.c | 65 +- tests/fast_regression | 1 + tests/regression/ust/Makefile.am | 2 +- .../ust/python-logging/LTTngTest.py | 76 +++ .../regression/ust/python-logging/Makefile.am | 21 + .../ust/python-logging/test_python_logging | 593 ++++++++++++++++++ tests/utils/utils.sh | 44 ++ 26 files changed, 993 insertions(+), 54 deletions(-) create mode 100644 tests/regression/ust/python-logging/LTTngTest.py create mode 100644 tests/regression/ust/python-logging/Makefile.am create mode 100755 tests/regression/ust/python-logging/test_python_logging diff --git a/configure.ac b/configure.ac index 487a2b978..8d1ff86ad 100644 --- a/configure.ac +++ b/configure.ac @@ -497,6 +497,7 @@ AC_CONFIG_FILES([ tests/regression/ust/libc-wrapper/Makefile tests/regression/ust/java-jul/Makefile tests/regression/ust/java-log4j/Makefile + tests/regression/ust/python-logging/Makefile tests/stress/Makefile tests/unit/Makefile tests/unit/ini_config/Makefile diff --git a/doc/man/lttng.1 b/doc/man/lttng.1 index f6d21e7ad..47f273144 100644 --- a/doc/man/lttng.1 +++ b/doc/man/lttng.1 @@ -27,10 +27,11 @@ kernel and/or instrumented applications (lttng-ust(3)). Aggregating and reading those traces is done using the babeltrace(1) text viewer. We introduce the notion of \fBtracing domains\fP which is essentially a type of -tracer (kernel, user space, JUL or LOG4J for now). In the future, we could see -more tracer like for instance an hypervisor. For some commands, you'll need to -specify on which domain the command operates (\-u, \-k or \-j). For instance, -the kernel domain must be specified when enabling a kernel event. +tracer (kernel, user space, JUL, LOG4J or Python for now). In the future, we +could see more tracer like for instance an hypervisor. For some commands, +you'll need to specify on which domain the command operates (\-u, \-k, \-l, \-j +or \-p). For instance, the kernel domain must be specified when enabling a +kernel event. In order to trace the kernel, the session daemon needs to be running as root. LTTng provides the use of a \fBtracing group\fP (default: tracing). Whomever is @@ -542,6 +543,9 @@ Apply for Java application using Java Util Logging interface (JUL) .BR "\-l, \-\-log4j" Apply for Java application using LOG4J .TP +.BR "\-p, \-\-python" +Apply for Python application using the logging module. +.TP .BR "\-\-tracepoint" Tracepoint event (default). Userspace tracer supports wildcards at the end of string. Don't forget to quote to deal with bash expansion. @@ -557,6 +561,8 @@ For the JUL domain, the loglevel ranges are detailed with the \-\-help option thus starting from SEVERE to FINEST. For the LOG4J domain, loglevels range from FATAL to TRACE which are also detailed in the help. +For the Python domain, loglevels range from CRITICAL to DEBUG which are +detailed in the help as well. .TP .BR "\-\-loglevel-only NAME" Tracepoint loglevel (only this loglevel). @@ -708,6 +714,9 @@ Apply for Java application using Java Util Logging interface (JUL) .TP .BR "\-l, \-\-log4j" Apply for Java application using LOG4J +.TP +.BR "\-p, \-\-python" +Apply for Python application using the logging module. .RE .PP @@ -728,6 +737,8 @@ With \-j alone, the available JUL event from registered application will be list. The event corresponds to the Logger name in the Java JUL application. With \-l alone, the available LOG4J event from registered application will be list. The event corresponds to the Logger name in the Java LOG4J application. +With \-p alone, the available Python event from registered application will be +list. The event corresponds to the Logger name in the Python application. With \-u alone, it will list all available user-space events from registered applications. Here is an example of 'lttng list \-u': @@ -758,9 +769,12 @@ Select user-space domain. .BR "\-j, \-\-jul" Apply for Java application using JUL .TP -.TP .BR "\-l, \-\-log4j" Apply for Java application using LOG4J +.TP +.BR "\-p, \-\-python" +Apply for Python application using the logging module. +.TP .BR "\-f, \-\-fields" List event fields diff --git a/include/lttng/domain.h b/include/lttng/domain.h index becebe2dd..3ee04191c 100644 --- a/include/lttng/domain.h +++ b/include/lttng/domain.h @@ -32,6 +32,7 @@ enum lttng_domain_type { LTTNG_DOMAIN_UST = 2, /* Global Userspace tracer. */ LTTNG_DOMAIN_JUL = 3, /* Java Util Logging. */ LTTNG_DOMAIN_LOG4J = 4, /* Java Log4j Framework. */ + LTTNG_DOMAIN_PYTHON = 5, /* Python logging Framework. */ }; /* Buffer type for a specific domain. */ diff --git a/include/lttng/event.h b/include/lttng/event.h index f1d8e6581..3e8fbe3e8 100644 --- a/include/lttng/event.h +++ b/include/lttng/event.h @@ -98,6 +98,19 @@ enum lttng_loglevel_log4j { LTTNG_LOGLEVEL_LOG4J_ALL = INT32_MIN, }; +/* + * Available loglevels for the Python domain. Those are an exact map from the + * Level class. + */ +enum lttng_loglevel_python { + LTTNG_LOGLEVEL_PYTHON_CRITICAL = 50, + LTTNG_LOGLEVEL_PYTHON_ERROR = 40, + LTTNG_LOGLEVEL_PYTHON_WARNING = 30, + LTTNG_LOGLEVEL_PYTHON_INFO = 20, + LTTNG_LOGLEVEL_PYTHON_DEBUG = 10, + LTTNG_LOGLEVEL_PYTHON_NOTSET = 0, +}; + /* * LTTng consumer mode */ diff --git a/src/bin/lttng-sessiond/agent.c b/src/bin/lttng-sessiond/agent.c index 1c1c6ab18..5cb8336c3 100644 --- a/src/bin/lttng-sessiond/agent.c +++ b/src/bin/lttng-sessiond/agent.c @@ -539,6 +539,8 @@ int agent_list_events(struct lttng_event **events, assert(events); + DBG2("Agent listing events for domain %d", domain); + nbmem = UST_APP_EVENT_LIST_SIZE; tmp_events = zmalloc(nbmem * sizeof(*tmp_events)); if (!tmp_events) { diff --git a/src/bin/lttng-sessiond/cmd.c b/src/bin/lttng-sessiond/cmd.c index 2a1bfb5f4..d882b074d 100644 --- a/src/bin/lttng-sessiond/cmd.c +++ b/src/bin/lttng-sessiond/cmd.c @@ -1138,6 +1138,7 @@ int cmd_disable_event(struct ltt_session *session, int domain, } case LTTNG_DOMAIN_LOG4J: case LTTNG_DOMAIN_JUL: + case LTTNG_DOMAIN_PYTHON: { struct agent *agt; struct ltt_ust_session *usess = session->ust_session; @@ -1542,6 +1543,7 @@ int cmd_enable_event(struct ltt_session *session, struct lttng_domain *domain, } case LTTNG_DOMAIN_LOG4J: case LTTNG_DOMAIN_JUL: + case LTTNG_DOMAIN_PYTHON: { const char *default_event_name, *default_chan_name; struct agent *agt; @@ -1581,10 +1583,19 @@ int cmd_enable_event(struct ltt_session *session, struct lttng_domain *domain, memcpy(&tmp_dom, domain, sizeof(tmp_dom)); tmp_dom.type = LTTNG_DOMAIN_UST; - if (domain->type == LTTNG_DOMAIN_LOG4J) { + switch (domain->type) { + case LTTNG_DOMAIN_LOG4J: default_chan_name = DEFAULT_LOG4J_CHANNEL_NAME; - } else { + break; + case LTTNG_DOMAIN_JUL: default_chan_name = DEFAULT_JUL_CHANNEL_NAME; + break; + case LTTNG_DOMAIN_PYTHON: + default_chan_name = DEFAULT_PYTHON_CHANNEL_NAME; + break; + default: + /* The switch/case we are in should avoid this else big problem */ + assert(0); } ret = cmd_enable_event(session, &tmp_dom, (char *) default_chan_name, @@ -1756,6 +1767,7 @@ ssize_t cmd_list_tracepoints(int domain, struct lttng_event **events) break; case LTTNG_DOMAIN_LOG4J: case LTTNG_DOMAIN_JUL: + case LTTNG_DOMAIN_PYTHON: nb_events = agent_list_events(events, domain); if (nb_events < 0) { ret = LTTNG_ERR_UST_LIST_FAIL; @@ -2492,6 +2504,7 @@ ssize_t cmd_list_events(int domain, struct ltt_session *session, } case LTTNG_DOMAIN_LOG4J: case LTTNG_DOMAIN_JUL: + case LTTNG_DOMAIN_PYTHON: if (session->ust_session) { struct lttng_ht_iter iter; struct agent *agt; diff --git a/src/bin/lttng-sessiond/event.c b/src/bin/lttng-sessiond/event.c index 099cbd18d..181926b41 100644 --- a/src/bin/lttng-sessiond/event.c +++ b/src/bin/lttng-sessiond/event.c @@ -715,26 +715,31 @@ const char *event_get_default_agent_ust_name(enum lttng_domain_type domain) { const char *default_event_name = NULL; - if (domain == LTTNG_DOMAIN_JUL) { + switch (domain) { + case LTTNG_DOMAIN_LOG4J: if (is_root) { - default_event_name = DEFAULT_SYS_JUL_EVENT_NAME; + default_event_name = DEFAULT_SYS_LOG4J_EVENT_NAME; } else { - default_event_name = DEFAULT_USER_JUL_EVENT_NAME; + default_event_name = DEFAULT_USER_LOG4J_EVENT_NAME; } - } else if (domain == LTTNG_DOMAIN_LOG4J) { + break; + case LTTNG_DOMAIN_JUL: if (is_root) { - default_event_name = DEFAULT_SYS_LOG4J_EVENT_NAME; + default_event_name = DEFAULT_SYS_JUL_EVENT_NAME; } else { - default_event_name = DEFAULT_USER_LOG4J_EVENT_NAME; + default_event_name = DEFAULT_USER_JUL_EVENT_NAME; } - } else { + break; + case LTTNG_DOMAIN_PYTHON: + default_event_name = DEFAULT_USER_PYTHON_EVENT_NAME; + break; + default: assert(0); } return default_event_name; } - /* * Disable a single agent event for a given UST session. * @@ -770,6 +775,8 @@ int event_agent_disable(struct ltt_ust_session *usess, struct agent *agt, ust_channel_name = DEFAULT_JUL_CHANNEL_NAME; } else if (agt->domain == LTTNG_DOMAIN_LOG4J) { ust_channel_name = DEFAULT_LOG4J_CHANNEL_NAME; + } else if (agt->domain == LTTNG_DOMAIN_PYTHON) { + ust_channel_name = DEFAULT_PYTHON_CHANNEL_NAME; } else { ret = LTTNG_ERR_INVALID; goto error; diff --git a/src/bin/lttng-sessiond/main.c b/src/bin/lttng-sessiond/main.c index fa09758ba..699d53323 100644 --- a/src/bin/lttng-sessiond/main.c +++ b/src/bin/lttng-sessiond/main.c @@ -2603,6 +2603,7 @@ static int copy_session_consumer(int domain, struct ltt_session *session) break; case LTTNG_DOMAIN_JUL: case LTTNG_DOMAIN_LOG4J: + case LTTNG_DOMAIN_PYTHON: case LTTNG_DOMAIN_UST: DBG3("Copying tracing session consumer output in UST session"); if (session->ust_session->consumer) { @@ -2648,6 +2649,7 @@ static int create_ust_session(struct ltt_session *session, switch (domain->type) { case LTTNG_DOMAIN_JUL: case LTTNG_DOMAIN_LOG4J: + case LTTNG_DOMAIN_PYTHON: case LTTNG_DOMAIN_UST: break; default: @@ -2895,6 +2897,7 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock, break; case LTTNG_DOMAIN_JUL: case LTTNG_DOMAIN_LOG4J: + case LTTNG_DOMAIN_PYTHON: case LTTNG_DOMAIN_UST: if (!cmd_ctx->session->ust_session) { ret = LTTNG_ERR_NO_CHANNEL; @@ -2977,6 +2980,7 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock, break; case LTTNG_DOMAIN_JUL: case LTTNG_DOMAIN_LOG4J: + case LTTNG_DOMAIN_PYTHON: case LTTNG_DOMAIN_UST: { if (!ust_app_supported()) { @@ -3071,6 +3075,7 @@ skip_domain: switch (cmd_ctx->lsm->domain.type) { case LTTNG_DOMAIN_JUL: case LTTNG_DOMAIN_LOG4J: + case LTTNG_DOMAIN_PYTHON: case LTTNG_DOMAIN_UST: if (uatomic_read(&ust_consumerd_state) != CONSUMER_STARTED) { ret = LTTNG_ERR_NO_USTCONSUMERD; diff --git a/src/bin/lttng-sessiond/save.c b/src/bin/lttng-sessiond/save.c index 908e5f8fd..aae56a895 100644 --- a/src/bin/lttng-sessiond/save.c +++ b/src/bin/lttng-sessiond/save.c @@ -1161,7 +1161,8 @@ int save_ust_session(struct config_writer *writer, ust_chan = caa_container_of(node, struct ltt_ust_channel, node); agent_channel = !strcmp(DEFAULT_JUL_CHANNEL_NAME, ust_chan->name) || - !strcmp(DEFAULT_LOG4J_CHANNEL_NAME, ust_chan->name); + !strcmp(DEFAULT_LOG4J_CHANNEL_NAME, ust_chan->name) || + !strcmp(DEFAULT_PYTHON_CHANNEL_NAME, ust_chan->name); if (!(save_agent ^ agent_channel)) { ret = save_ust_channel(writer, ust_chan, session->ust_session); if (ret) { diff --git a/src/bin/lttng/commands/disable_events.c b/src/bin/lttng/commands/disable_events.c index 1ce687aa4..e833fe957 100644 --- a/src/bin/lttng/commands/disable_events.c +++ b/src/bin/lttng/commands/disable_events.c @@ -37,6 +37,7 @@ static int opt_userspace; static int opt_disable_all; static int opt_jul; static int opt_log4j; +static int opt_python; static int opt_event_type; #if 0 /* Not implemented yet */ @@ -62,6 +63,7 @@ static struct poptOption long_options[] = { {"channel", 'c', POPT_ARG_STRING, &opt_channel_name, 0, 0, 0}, {"jul", 'j', POPT_ARG_VAL, &opt_jul, 1, 0, 0}, {"log4j", 'l', POPT_ARG_VAL, &opt_log4j, 1, 0, 0}, + {"python", 'p', POPT_ARG_VAL, &opt_python, 1, 0, 0}, {"kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0}, {"syscall", 0, POPT_ARG_NONE, 0, OPT_SYSCALL, 0, 0}, #if 0 @@ -92,6 +94,7 @@ static void usage(FILE *ofp) fprintf(ofp, " -u, --userspace Apply to the user-space tracer\n"); fprintf(ofp, " -j, --jul Apply for Java application using JUL\n"); fprintf(ofp, " -l, --log4j Apply to Java application using LOG4j\n"); + fprintf(ofp, " -p, --python Apply to Python application using logging\n"); fprintf(ofp, "\n"); fprintf(ofp, "Event options:\n"); fprintf(ofp, " --syscall System call event\n"); @@ -178,6 +181,8 @@ static int disable_events(char *session_name) dom.type = LTTNG_DOMAIN_JUL; } else if (opt_log4j) { dom.type = LTTNG_DOMAIN_LOG4J; + } else if (opt_python) { + dom.type = LTTNG_DOMAIN_PYTHON; } else { print_missing_domain(); ret = CMD_ERROR; diff --git a/src/bin/lttng/commands/enable_events.c b/src/bin/lttng/commands/enable_events.c index 8b1215b11..84bdb83c8 100644 --- a/src/bin/lttng/commands/enable_events.c +++ b/src/bin/lttng/commands/enable_events.c @@ -47,6 +47,7 @@ static char *opt_session_name; static int opt_userspace; static int opt_jul; static int opt_log4j; +static int opt_python; static int opt_enable_all; static char *opt_probe; static char *opt_function; @@ -88,6 +89,7 @@ static struct poptOption long_options[] = { {"userspace", 'u', POPT_ARG_NONE, 0, OPT_USERSPACE, 0, 0}, {"jul", 'j', POPT_ARG_VAL, &opt_jul, 1, 0, 0}, {"log4j", 'l', POPT_ARG_VAL, &opt_log4j, 1, 0, 0}, + {"python", 'p', POPT_ARG_VAL, &opt_python, 1, 0, 0}, {"tracepoint", 0, POPT_ARG_NONE, 0, OPT_TRACEPOINT, 0, 0}, {"probe", 0, POPT_ARG_STRING, &opt_probe, OPT_PROBE, 0, 0}, {"function", 0, POPT_ARG_STRING, &opt_function, OPT_FUNCTION, 0, 0}, @@ -124,6 +126,7 @@ static void usage(FILE *ofp) fprintf(ofp, " -u, --userspace Apply to the user-space tracer\n"); fprintf(ofp, " -j, --jul Apply for Java application using JUL\n"); fprintf(ofp, " -l, --log4j Apply for Java application using LOG4j\n"); + fprintf(ofp, " -p, --python Apply for Java application using LOG4j\n"); fprintf(ofp, "\n"); fprintf(ofp, "Event options:\n"); fprintf(ofp, " --tracepoint Tracepoint event (default)\n"); @@ -197,6 +200,15 @@ static void usage(FILE *ofp) fprintf(ofp, " LOG4J_ALL = INT32_MIN\n"); fprintf(ofp, " (shortcuts such as \"severe\" are allowed)\n"); fprintf(ofp, "\n"); + fprintf(ofp, " Available Python domain loglevels:\n"); + fprintf(ofp, " PYTHON_CRITICAL = %d\n", LTTNG_LOGLEVEL_PYTHON_CRITICAL); + fprintf(ofp, " PYTHON_ERROR = %d\n", LTTNG_LOGLEVEL_PYTHON_ERROR); + fprintf(ofp, " PYTHON_WARNING = %d\n", LTTNG_LOGLEVEL_PYTHON_WARNING); + fprintf(ofp, " PYTHON_INFO = %d\n", LTTNG_LOGLEVEL_PYTHON_INFO); + fprintf(ofp, " PYTHON_DEBUG = %d\n", LTTNG_LOGLEVEL_PYTHON_DEBUG); + fprintf(ofp, " PYTHON_NOTSET = %d\n", LTTNG_LOGLEVEL_PYTHON_NOTSET); + fprintf(ofp, " (shortcuts such as \"critical\" are allowed)\n"); + fprintf(ofp, "\n"); fprintf(ofp, " -f, --filter \'expression\'\n"); fprintf(ofp, " Filter expression on event fields and context.\n"); fprintf(ofp, " Event recording depends on evaluation.\n"); @@ -393,6 +405,41 @@ static int loglevel_jul_str_to_value(const char *inputstr) } } +/* + * Maps Python loglevel from string to value + */ +static int loglevel_python_str_to_value(const char *inputstr) +{ + int i = 0; + char str[LTTNG_SYMBOL_NAME_LEN]; + + /* + * Loop up to LTTNG_SYMBOL_NAME_LEN minus one because the NULL bytes is + * added at the end of the loop so a the upper bound we avoid the overflow. + */ + while (i < (LTTNG_SYMBOL_NAME_LEN - 1) && inputstr[i] != '\0') { + str[i] = toupper(inputstr[i]); + i++; + } + str[i] = '\0'; + + if (!strcmp(str, "PYTHON_CRITICAL") || !strcmp(str, "CRITICAL")) { + return LTTNG_LOGLEVEL_PYTHON_CRITICAL; + } else if (!strcmp(str, "PYTHON_ERROR") || !strcmp(str, "ERROR")) { + return LTTNG_LOGLEVEL_PYTHON_ERROR; + } else if (!strcmp(str, "PYTHON_WARNING") || !strcmp(str, "WARNING")) { + return LTTNG_LOGLEVEL_PYTHON_WARNING; + } else if (!strcmp(str, "PYTHON_INFO") || !strcmp(str, "INFO")) { + return LTTNG_LOGLEVEL_PYTHON_INFO; + } else if (!strcmp(str, "PYTNON_DEBUG") || !strcmp(str, "DEBUG")) { + return LTTNG_LOGLEVEL_PYTHON_DEBUG; + } else if (!strcmp(str, "PYTHON_NOTSET") || !strcmp(str, "NOTSET")) { + return LTTNG_LOGLEVEL_PYTHON_NOTSET; + } else { + return -1; + } +} + /* * Maps loglevel from string to value */ @@ -658,6 +705,10 @@ static int enable_events(char *session_name) dom.type = LTTNG_DOMAIN_LOG4J; /* Default. */ dom.buf_type = LTTNG_BUFFER_PER_UID; + } else if (opt_python) { + dom.type = LTTNG_DOMAIN_PYTHON; + /* Default. */ + dom.buf_type = LTTNG_BUFFER_PER_UID; } else { print_missing_domain(); ret = CMD_ERROR; @@ -700,13 +751,15 @@ static int enable_events(char *session_name) strcpy(ev.name, "*"); ev.loglevel_type = opt_loglevel_type; if (opt_loglevel) { - assert(opt_userspace || opt_jul || opt_log4j); + assert(opt_userspace || opt_jul || opt_log4j || opt_python); if (opt_userspace) { ev.loglevel = loglevel_str_to_value(opt_loglevel); } else if (opt_jul) { ev.loglevel = loglevel_jul_str_to_value(opt_loglevel); } else if (opt_log4j) { ev.loglevel = loglevel_log4j_str_to_value(opt_loglevel); + } else if (opt_python) { + ev.loglevel = loglevel_python_str_to_value(opt_loglevel); } if (ev.loglevel == -1) { ERR("Unknown loglevel %s", opt_loglevel); @@ -714,11 +767,13 @@ static int enable_events(char *session_name) goto error; } } else { - assert(opt_userspace || opt_jul || opt_log4j); + assert(opt_userspace || opt_jul || opt_log4j || opt_python); if (opt_userspace) { ev.loglevel = -1; } else if (opt_jul || opt_log4j) { ev.loglevel = LTTNG_LOGLEVEL_JUL_ALL; + } else if (opt_python) { + ev.loglevel = LTTNG_LOGLEVEL_PYTHON_DEBUG; } } } @@ -1002,7 +1057,7 @@ static int enable_events(char *session_name) } else { ev.loglevel = -1; } - } else if (opt_jul || opt_log4j) { + } else if (opt_jul || opt_log4j || opt_python) { if (opt_event_type != LTTNG_EVENT_ALL && opt_event_type != LTTNG_EVENT_TRACEPOINT) { ERR("Event type not supported for domain."); @@ -1016,6 +1071,8 @@ static int enable_events(char *session_name) ev.loglevel = loglevel_jul_str_to_value(opt_loglevel); } else if (opt_log4j) { ev.loglevel = loglevel_log4j_str_to_value(opt_loglevel); + } else if (opt_python) { + ev.loglevel = loglevel_python_str_to_value(opt_loglevel); } if (ev.loglevel == -1) { ERR("Unknown loglevel %s", opt_loglevel); @@ -1027,6 +1084,8 @@ static int enable_events(char *session_name) ev.loglevel = LTTNG_LOGLEVEL_JUL_ALL; } else if (opt_log4j) { ev.loglevel = LTTNG_LOGLEVEL_LOG4J_ALL; + } else if (opt_python) { + ev.loglevel = LTTNG_LOGLEVEL_PYTHON_DEBUG; } } ev.type = LTTNG_EVENT_TRACEPOINT; diff --git a/src/bin/lttng/commands/list.c b/src/bin/lttng/commands/list.c index ea9427f47..7c3211a59 100644 --- a/src/bin/lttng/commands/list.c +++ b/src/bin/lttng/commands/list.c @@ -31,6 +31,7 @@ static int opt_userspace; static int opt_kernel; static int opt_jul; static int opt_log4j; +static int opt_python; static char *opt_channel; static int opt_domain; static int opt_fields; @@ -60,6 +61,7 @@ static struct poptOption long_options[] = { {"kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0}, {"jul", 'j', POPT_ARG_VAL, &opt_jul, 1, 0, 0}, {"log4j", 'l', POPT_ARG_VAL, &opt_log4j, 1, 0, 0}, + {"python", 'p', POPT_ARG_VAL, &opt_python, 1, 0, 0}, #if 0 /* Not implemented yet */ {"userspace", 'u', POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, &opt_cmd_name, OPT_USERSPACE, 0, 0}, @@ -93,6 +95,7 @@ static void usage(FILE *ofp) fprintf(ofp, " -u, --userspace Select user-space domain.\n"); fprintf(ofp, " -j, --jul Apply for Java application using JUL\n"); fprintf(ofp, " -l, --log4j Apply for Java application using LOG4J\n"); + fprintf(ofp, " -p, --python Apply for Python application using logging\n"); fprintf(ofp, " -f, --fields List event fields.\n"); fprintf(ofp, " --syscall List available system calls.\n"); #if 0 @@ -427,6 +430,8 @@ static int list_agent_events(void) domain.type = LTTNG_DOMAIN_JUL; } else if (opt_log4j) { domain.type = LTTNG_DOMAIN_LOG4J; + } else if (opt_python) { + domain.type = LTTNG_DOMAIN_PYTHON; } agent_domain_str = get_domain_str(domain.type); @@ -1469,6 +1474,9 @@ static int list_domains(const char *session_name) case LTTNG_DOMAIN_LOG4J: MSG(" - LOG4j (Logging for Java)"); break; + case LTTNG_DOMAIN_PYTHON: + MSG(" - Python (logging)"); + break; default: break; } @@ -1561,6 +1569,8 @@ int cmd_list(int argc, const char **argv) domain.type = LTTNG_DOMAIN_JUL; } else if (opt_log4j) { domain.type = LTTNG_DOMAIN_LOG4J; + } else if (opt_python) { + domain.type = LTTNG_DOMAIN_PYTHON; } if (!opt_kernel && opt_syscall) { @@ -1569,7 +1579,7 @@ int cmd_list(int argc, const char **argv) goto end; } - if (opt_kernel || opt_userspace || opt_jul || opt_log4j) { + if (opt_kernel || opt_userspace || opt_jul || opt_log4j || opt_python) { handle = lttng_create_handle(session_name, &domain); if (handle == NULL) { ret = CMD_FATAL; @@ -1578,7 +1588,8 @@ int cmd_list(int argc, const char **argv) } if (session_name == NULL) { - if (!opt_kernel && !opt_userspace && !opt_jul && !opt_log4j) { + if (!opt_kernel && !opt_userspace && !opt_jul && !opt_log4j + && !opt_python) { ret = list_sessions(NULL); if (ret) { goto end; @@ -1607,7 +1618,7 @@ int cmd_list(int argc, const char **argv) goto end; } } - if (opt_jul || opt_log4j) { + if (opt_jul || opt_log4j || opt_python) { ret = list_agent_events(); if (ret) { goto end; @@ -1705,6 +1716,9 @@ int cmd_list(int argc, const char **argv) case LTTNG_DOMAIN_LOG4J: MSG("=== Domain: LOG4j (Logging for Java) ===\n"); break; + case LTTNG_DOMAIN_PYTHON: + MSG("=== Domain: Python (logging) ===\n"); + break; default: MSG("=== Domain: Unimplemented ===\n"); break; @@ -1730,7 +1744,8 @@ int cmd_list(int argc, const char **argv) } if (domains[i].type == LTTNG_DOMAIN_JUL || - domains[i].type == LTTNG_DOMAIN_LOG4J) { + domains[i].type == LTTNG_DOMAIN_LOG4J || + domains[i].type == LTTNG_DOMAIN_PYTHON) { ret = list_session_agent_events(); if (ret) { goto end; diff --git a/src/bin/lttng/utils.c b/src/bin/lttng/utils.c index fc592ebde..c518405ca 100644 --- a/src/bin/lttng/utils.c +++ b/src/bin/lttng/utils.c @@ -37,6 +37,7 @@ static const char *str_kernel = "Kernel"; static const char *str_ust = "UST"; static const char *str_jul = "JUL"; static const char *str_log4j = "LOG4J"; +static const char *str_python = "Python"; /* * get_session_name @@ -278,6 +279,9 @@ const char *get_domain_str(enum lttng_domain_type domain) case LTTNG_DOMAIN_LOG4J: str_dom = str_log4j; break; + case LTTNG_DOMAIN_PYTHON: + str_dom = str_python; + break; default: /* Should not have an unknown domain or else define it. */ assert(0); diff --git a/src/common/config/config.c b/src/common/config/config.c index e44710b5a..feb3334cf 100644 --- a/src/common/config/config.c +++ b/src/common/config/config.c @@ -118,6 +118,7 @@ const char * const config_domain_type_kernel = "KERNEL"; const char * const config_domain_type_ust = "UST"; const char * const config_domain_type_jul = "JUL"; const char * const config_domain_type_log4j = "LOG4J"; +const char * const config_domain_type_python = "PYTHON"; const char * const config_buffer_type_per_pid = "PER_PID"; const char * const config_buffer_type_per_uid = "PER_UID"; @@ -752,6 +753,8 @@ int get_domain_type(xmlChar *domain) ret = LTTNG_DOMAIN_JUL; } else if (!strcmp((char *) domain, config_domain_type_log4j)) { ret = LTTNG_DOMAIN_LOG4J; + } else if (!strcmp((char *) domain, config_domain_type_python)) { + ret = LTTNG_DOMAIN_PYTHON; } else { goto error; } @@ -2154,6 +2157,7 @@ int process_session_node(xmlNodePtr session_node, const char *session_name, struct lttng_domain *ust_domain = NULL; struct lttng_domain *jul_domain = NULL; struct lttng_domain *log4j_domain = NULL; + struct lttng_domain *python_domain = NULL; for (node = xmlFirstElementChild(session_node); node; node = xmlNextElementSibling(node)) { @@ -2287,6 +2291,13 @@ int process_session_node(xmlNodePtr session_node, const char *session_name, } log4j_domain = domain; break; + case LTTNG_DOMAIN_PYTHON: + if (python_domain) { + /* Same domain seen twice, invalid! */ + goto domain_init_error; + } + python_domain = domain; + break; default: WARN("Invalid domain type"); goto domain_init_error; diff --git a/src/common/config/session.xsd b/src/common/config/session.xsd index 3023216ea..6d74e85f7 100644 --- a/src/common/config/session.xsd +++ b/src/common/config/session.xsd @@ -193,6 +193,7 @@ elementFormDefault="qualified" version="2.5"> + diff --git a/src/common/defaults.h b/src/common/defaults.h index 25d7b327b..c7e801d14 100644 --- a/src/common/defaults.h +++ b/src/common/defaults.h @@ -175,7 +175,11 @@ #define DEFAULT_SYS_LOG4J_EVENT_NAME "lttng_log4j:sys*" #define DEFAULT_USER_LOG4J_EVENT_NAME "lttng_log4j:user*" -/* JUL default channel name. */ +/* Default Python domain channel name. */ +#define DEFAULT_PYTHON_CHANNEL_NAME "lttng_python_channel" +/* Default Python tracepoint name. This is a wildcard for the python domain. */ +#define DEFAULT_USER_PYTHON_EVENT_NAME "lttng_python:user*" + #define DEFAULT_CHANNEL_OVERWRITE 0 #define DEFAULT_CHANNEL_TRACEFILE_SIZE 0 #define DEFAULT_CHANNEL_TRACEFILE_COUNT 0 diff --git a/src/common/mi-lttng.c b/src/common/mi-lttng.c index c21ed5a63..882afc1b6 100644 --- a/src/common/mi-lttng.c +++ b/src/common/mi-lttng.c @@ -135,6 +135,14 @@ const char * const mi_lttng_loglevel_str_log4j_debug = "LOG4J_DEBUG"; const char * const mi_lttng_loglevel_str_log4j_trace = "LOG4J_TRACE"; const char * const mi_lttng_loglevel_str_log4j_all = "LOG4J_ALL"; +/* String related to loglevel Python */ +const char * const mi_lttng_loglevel_str_python_critical = "PYTHON_CRITICAL"; +const char * const mi_lttng_loglevel_str_python_error = "PYTHON_ERROR"; +const char * const mi_lttng_loglevel_str_python_warning = "PYTHON_WARNING"; +const char * const mi_lttng_loglevel_str_python_info = "PYTHON_INFO"; +const char * const mi_lttng_loglevel_str_python_debug = "PYTHON_DEBUG"; +const char * const mi_lttng_loglevel_str_python_notset = "PYTHON_NOTSET"; + /* String related to loglevel type */ const char * const mi_lttng_loglevel_type_all = "ALL"; const char * const mi_lttng_loglevel_type_range = "RANGE"; @@ -250,6 +258,24 @@ const char *mi_lttng_loglevel_string(int value, enum lttng_domain_type domain) return mi_lttng_loglevel_str_unknown; } break; + case LTTNG_DOMAIN_PYTHON: + switch (value) { + case LTTNG_LOGLEVEL_PYTHON_CRITICAL: + return mi_lttng_loglevel_str_python_critical; + case LTTNG_LOGLEVEL_PYTHON_ERROR: + return mi_lttng_loglevel_str_python_error; + case LTTNG_LOGLEVEL_PYTHON_WARNING: + return mi_lttng_loglevel_str_python_warning; + case LTTNG_LOGLEVEL_PYTHON_INFO: + return mi_lttng_loglevel_str_python_info; + case LTTNG_LOGLEVEL_PYTHON_DEBUG: + return mi_lttng_loglevel_str_python_debug; + case LTTNG_LOGLEVEL_PYTHON_NOTSET: + return mi_lttng_loglevel_str_python_notset; + default: + return mi_lttng_loglevel_str_unknown; + } + break; } /* Reaching this means the domain is unknown. */ diff --git a/src/common/mi-lttng.h b/src/common/mi-lttng.h index a181e0b10..fd1ef936e 100644 --- a/src/common/mi-lttng.h +++ b/src/common/mi-lttng.h @@ -159,6 +159,14 @@ const char * const mi_lttng_loglevel_str_log4j_debug; const char * const mi_lttng_loglevel_str_log4j_trace; const char * const mi_lttng_loglevel_str_log4j_all; +/* String related to loglevel Python */ +const char * const mi_lttng_loglevel_str_python_critical; +const char * const mi_lttng_loglevel_str_python_error; +const char * const mi_lttng_loglevel_str_python_warning; +const char * const mi_lttng_loglevel_str_python_info; +const char * const mi_lttng_loglevel_str_python_debug; +const char * const mi_lttng_loglevel_str_python_notset; + /* String related to loglevel type */ const char * const mi_lttng_loglevel_type_all; const char * const mi_lttng_loglevel_type_range; diff --git a/src/common/mi_lttng.xsd b/src/common/mi_lttng.xsd index f5fc7293c..3f0894e83 100644 --- a/src/common/mi_lttng.xsd +++ b/src/common/mi_lttng.xsd @@ -140,6 +140,12 @@ THE SOFTWARE. + + + + + + @@ -228,6 +234,7 @@ THE SOFTWARE. + diff --git a/src/lib/lttng-ctl/lttng-ctl.c b/src/lib/lttng-ctl/lttng-ctl.c index ff57a0d1a..aae0e4fa5 100644 --- a/src/lib/lttng-ctl/lttng-ctl.c +++ b/src/lib/lttng-ctl/lttng-ctl.c @@ -106,6 +106,7 @@ void lttng_ctl_copy_lttng_domain(struct lttng_domain *dst, case LTTNG_DOMAIN_UST: case LTTNG_DOMAIN_JUL: case LTTNG_DOMAIN_LOG4J: + case LTTNG_DOMAIN_PYTHON: memcpy(dst, src, sizeof(struct lttng_domain)); break; default: @@ -690,25 +691,25 @@ int lttng_enable_event_with_filter(struct lttng_handle *handle, } /* - * Depending on the event, return a newly allocated JUL filter expression or + * Depending on the event, return a newly allocated agent filter expression or * NULL if not applicable. * * An event with NO loglevel and the name is * will return NULL. */ -static char *set_jul_filter(const char *filter, struct lttng_event *ev) +static char *set_agent_filter(const char *filter, struct lttng_event *ev) { int err; - char *jul_filter = NULL; + char *agent_filter = NULL; assert(ev); /* Don't add filter for the '*' event. */ if (ev->name[0] != '*') { if (filter) { - err = asprintf(&jul_filter, "(%s) && (logger_name == \"%s\")", filter, + err = asprintf(&agent_filter, "(%s) && (logger_name == \"%s\")", filter, ev->name); } else { - err = asprintf(&jul_filter, "logger_name == \"%s\"", ev->name); + err = asprintf(&agent_filter, "logger_name == \"%s\"", ev->name); } if (err < 0) { PERROR("asprintf"); @@ -726,18 +727,18 @@ static char *set_jul_filter(const char *filter, struct lttng_event *ev) op = "=="; } - if (filter || jul_filter) { + if (filter || agent_filter) { char *new_filter; err = asprintf(&new_filter, "(%s) && (int_loglevel %s %d)", - jul_filter ? jul_filter : filter, op, + agent_filter ? agent_filter : filter, op, ev->loglevel); - if (jul_filter) { - free(jul_filter); + if (agent_filter) { + free(agent_filter); } - jul_filter = new_filter; + agent_filter = new_filter; } else { - err = asprintf(&jul_filter, "int_loglevel %s %d", op, + err = asprintf(&agent_filter, "int_loglevel %s %d", op, ev->loglevel); } if (err < 0) { @@ -746,9 +747,9 @@ static char *set_jul_filter(const char *filter, struct lttng_event *ev) } } - return jul_filter; + return agent_filter; error: - free(jul_filter); + free(agent_filter); return NULL; } @@ -941,7 +942,8 @@ int lttng_enable_event_with_exclusions(struct lttng_handle *handle, */ if (exclusion_count == 0 && filter_expression == NULL && (handle->domain.type != LTTNG_DOMAIN_JUL && - handle->domain.type != LTTNG_DOMAIN_LOG4J)) { + handle->domain.type != LTTNG_DOMAIN_LOG4J && + handle->domain.type != LTTNG_DOMAIN_PYTHON)) { goto ask_sessiond; } @@ -952,24 +954,26 @@ int lttng_enable_event_with_exclusions(struct lttng_handle *handle, /* Parse filter expression */ if (filter_expression != NULL || handle->domain.type == LTTNG_DOMAIN_JUL - || handle->domain.type == LTTNG_DOMAIN_LOG4J) { + || handle->domain.type == LTTNG_DOMAIN_LOG4J + || handle->domain.type == LTTNG_DOMAIN_PYTHON) { if (handle->domain.type == LTTNG_DOMAIN_JUL || - handle->domain.type == LTTNG_DOMAIN_LOG4J) { - char *jul_filter; + handle->domain.type == LTTNG_DOMAIN_LOG4J || + handle->domain.type == LTTNG_DOMAIN_PYTHON) { + char *agent_filter; /* Setup JUL filter if needed. */ - jul_filter = set_jul_filter(filter_expression, ev); - if (!jul_filter) { + agent_filter = set_agent_filter(filter_expression, ev); + if (!agent_filter) { if (!filter_expression) { /* No JUL and no filter, just skip everything below. */ goto ask_sessiond; } } else { /* - * With a JUL filter, the original filter has been added to it - * thus replace the filter expression. + * With an agent filter, the original filter has been added to + * it thus replace the filter expression. */ - filter_expression = jul_filter; + filter_expression = agent_filter; free_filter_expression = 1; } } @@ -1102,7 +1106,8 @@ int lttng_disable_event_ext(struct lttng_handle *handle, */ if (filter_expression == NULL && (handle->domain.type != LTTNG_DOMAIN_JUL && - handle->domain.type != LTTNG_DOMAIN_LOG4J)) { + handle->domain.type != LTTNG_DOMAIN_LOG4J && + handle->domain.type != LTTNG_DOMAIN_PYTHON)) { goto ask_sessiond; } @@ -1113,14 +1118,16 @@ int lttng_disable_event_ext(struct lttng_handle *handle, /* Parse filter expression */ if (filter_expression != NULL || handle->domain.type == LTTNG_DOMAIN_JUL - || handle->domain.type == LTTNG_DOMAIN_LOG4J) { + || handle->domain.type == LTTNG_DOMAIN_LOG4J + || handle->domain.type == LTTNG_DOMAIN_PYTHON) { if (handle->domain.type == LTTNG_DOMAIN_JUL || - handle->domain.type == LTTNG_DOMAIN_LOG4J) { - char *jul_filter; + handle->domain.type == LTTNG_DOMAIN_LOG4J || + handle->domain.type == LTTNG_DOMAIN_PYTHON) { + char *agent_filter; /* Setup JUL filter if needed. */ - jul_filter = set_jul_filter(filter_expression, ev); - if (!jul_filter) { + agent_filter = set_agent_filter(filter_expression, ev); + if (!agent_filter) { if (!filter_expression) { /* No JUL and no filter, just skip everything below. */ goto ask_sessiond; @@ -1130,7 +1137,7 @@ int lttng_disable_event_ext(struct lttng_handle *handle, * With a JUL filter, the original filter has been added to it * thus replace the filter expression. */ - filter_expression = jul_filter; + filter_expression = agent_filter; free_filter_expression = 1; } } diff --git a/tests/fast_regression b/tests/fast_regression index af478abda..c14f7f215 100644 --- a/tests/fast_regression +++ b/tests/fast_regression @@ -21,6 +21,7 @@ regression/ust/nprocesses/test_nprocesses regression/ust/overlap/test_overlap regression/ust/java-jul/test_java_jul regression/ust/java-log4j/test_java_log4j +regression/ust/python-logging/test_python_logging regression/ust/test_event_basic regression/ust/test_event_tracef regression/ust/test_event_wildcard diff --git a/tests/regression/ust/Makefile.am b/tests/regression/ust/Makefile.am index 273994a77..0d11f9088 100644 --- a/tests/regression/ust/Makefile.am +++ b/tests/regression/ust/Makefile.am @@ -1,7 +1,7 @@ if HAVE_LIBLTTNG_UST_CTL SUBDIRS = nprocesses high-throughput low-throughput before-after multi-session \ overlap buffers-pid linking daemon exit-fast fork libc-wrapper \ - periodical-metadata-flush java-jul java-log4j + periodical-metadata-flush java-jul java-log4j python-logging EXTRA_DIST = test_event_basic test_event_wildcard test_event_tracef test_event_perf diff --git a/tests/regression/ust/python-logging/LTTngTest.py b/tests/regression/ust/python-logging/LTTngTest.py new file mode 100644 index 000000000..a3ed8f99e --- /dev/null +++ b/tests/regression/ust/python-logging/LTTngTest.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python +# +# Copyright (C) 2014 - David Goulet +# +# This library is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; version 2.1 of the License. +# +# This library 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 Lesser General Public License for more +# details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +import sys +import logging +import errno + +from time import sleep + +def cleanup(code, agent = None): + """ + Cleanup agent and exit with given code. + """ + if agent is not None: + agent.destroy() + + sys.exit(code) + +try: + import lttng_agent +except ImportError as e: + print("LTTng Agent not found. Aborting") + cleanup(errno.ENOSYS) + +def run(): + """ + Main for this test program. Based on the Java testing program that behaves + exactly the same. + """ + + agent = lttng_agent.LTTngAgent() + ev1 = logging.getLogger("python-ev-test1"); + ev2 = logging.getLogger("python-ev-test2"); + + try: + nr_iter = int(sys.argv[1]) + wait_time = int(sys.argv[2]) + fire_debug_ev = 0 + fire_second_ev = 0 + except IndexError as e: + print("Missing arguments. Aborting") + cleanup(errno.EINVAL, agent) + except ValueError as e: + print("Invalid arguments. Aborting") + cleanup(errno.EINVAL, agent) + + if len(sys.argv) > 3: + fire_debug_ev = int(sys.argv[3]) + if len(sys.argv) > 4: + fire_second_ev = int(sys.argv[4]) + + for i in range(0, nr_iter): + ev1.info("%s fired" % ev1.name) + if fire_debug_ev != 0: + ev1.debug("%s DEBUG fired" % ev1.name) + sleep(wait_time) + + if fire_second_ev != 0: + ev2.info("%s fired" % ev2.name) + +if __name__ == "__main__": + run() diff --git a/tests/regression/ust/python-logging/Makefile.am b/tests/regression/ust/python-logging/Makefile.am new file mode 100644 index 000000000..34a8deee1 --- /dev/null +++ b/tests/regression/ust/python-logging/Makefile.am @@ -0,0 +1,21 @@ +#if USE_PYTHON + +noinst_SCRIPTS = test_python_logging +EXTRA_DIST = test_python_logging LTTngPython.py + +all-local: + @if [ x"$(srcdir)" != x"$(builddir)" ]; then \ + for script in $(EXTRA_DIST); do \ + cp -f $(srcdir)/$$script $(builddir); \ + done; \ + fi + +clean-local: + rm -f *.class + @if [ x"$(srcdir)" != x"$(builddir)" ]; then \ + for script in $(EXTRA_DIST); do \ + rm -f $(builddir)/$$script; \ + done; \ + fi + +#endif diff --git a/tests/regression/ust/python-logging/test_python_logging b/tests/regression/ust/python-logging/test_python_logging new file mode 100755 index 000000000..b2fa6fb76 --- /dev/null +++ b/tests/regression/ust/python-logging/test_python_logging @@ -0,0 +1,593 @@ +#!/bin/bash +# +# Copyright (C) - 2014 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. + +TEST_DESC="Java Python support" + +CURDIR=$(dirname $0)/ +TESTDIR=$CURDIR/../../.. +NR_ITER=5 +NR_SEC_WAIT=1 +TESTAPP_NAME="LTTngTest" +TESTAPP_BIN="$TESTAPP_NAME.py" +TESTAPP_PATH="$CURDIR" +SESSION_NAME="python-test" +EVENT_NAME="python-ev-test1" +EVENT_NAME2="python-ev-test2" +OUTPUT_DEST="/dev/null" + +NUM_TESTS=156 + +source $TESTDIR/utils/utils.sh + +function run_app +{ + local debug_tp=$1 + local fire_second_tp=$2 + + python $TESTAPP_PATH/$TESTAPP_BIN $NR_ITER $NR_SEC_WAIT $debug_tp $fire_second_tp +} + +function run_app_background +{ + run_app $@ & +} + +function enable_python_loglevel_only() +{ + sess_name=$1 + event_name="$2" + loglevel=$3 + channel_name=$4 + + if [ -z $channel_name ]; then + # default channel if none specified + chan="" + else + chan="-c $channel_name" + fi + + $TESTDIR/../src/bin/lttng/$LTTNG_BIN enable-event --loglevel-only $loglevel "$event_name" $chan -s $sess_name -p >$OUTPUT_DEST + ok $? "Enable Python event $event_name for session $sess_name with loglevel-only $loglevel" +} + +function enable_python_filter() +{ + local sess_name="$1" + local event_name="$2" + local filter="$3" + + $TESTDIR/../src/bin/lttng/$LTTNG_BIN enable-event "$event_name" -s $sess_name -p --filter "$filter" >/dev/null 2>&1 + ok $? "Enable event $event_name with filter $filter for session $sess_name" +} + +function enable_python_filter_loglevel_only() +{ + local sess_name="$1" + local event_name="$2" + local filter="$3" + local loglevel="$4" + + $TESTDIR/../src/bin/lttng/$LTTNG_BIN enable-event --loglevel-only $loglevel "$event_name" -s $sess_name -p --filter "$filter" >$OUTPUT_DEST + ok $? "Enable event $event_name with filter \"$filter\" and loglevel-only $loglevel for session $sess_name" +} + +# MUST set TESTDIR before calling those functions + +function test_python_before_start () +{ + diag "Test Python application BEFORE tracing starts" + create_lttng_session $SESSION_NAME $TRACE_PATH + enable_python_lttng_event $SESSION_NAME $EVENT_NAME + + # Run 5 times with a 1 second delay + run_app_background + + start_lttng_tracing $SESSION_NAME + + # Wait for the applications started in background + wait ${!} + + stop_lttng_tracing $SESSION_NAME + destroy_lttng_session $SESSION_NAME + + # Validate test. Expecting all events. + trace_match_only $EVENT_NAME $NR_ITER $TRACE_PATH + if [ $? -ne 0 ]; then + return $? + fi +} + +function test_python_after_start () +{ + diag "Test Python application AFTER tracing starts" + + create_lttng_session $SESSION_NAME $TRACE_PATH + enable_python_lttng_event $SESSION_NAME $EVENT_NAME + start_lttng_tracing $SESSION_NAME + + # Run 5 times with a 1 second delay + run_app + + stop_lttng_tracing $SESSION_NAME + destroy_lttng_session $SESSION_NAME + + # Validate test. Expecting all events. + trace_match_only $EVENT_NAME $NR_ITER $TRACE_PATH + if [ $? -ne 0 ]; then + return $? + fi +} + +function test_python_loglevel () +{ + diag "Test Python application with loglevel" + + create_lttng_session $SESSION_NAME $TRACE_PATH + enable_python_lttng_event_loglevel $SESSION_NAME $EVENT_NAME "INFO" + start_lttng_tracing $SESSION_NAME + + # Run 5 times with a 1 second delay + run_app + + stop_lttng_tracing $SESSION_NAME + destroy_lttng_session $SESSION_NAME + + # Validate test. Expecting all events. + trace_match_only $EVENT_NAME $NR_ITER $TRACE_PATH + if [ $? -ne 0 ]; then + return $? + fi + + diag "Test Python applications with lower loglevel" + + create_lttng_session $SESSION_NAME $TRACE_PATH + enable_python_lttng_event_loglevel $SESSION_NAME $EVENT_NAME "CRITICAL" + start_lttng_tracing $SESSION_NAME + + # Run 5 times with a 1 second delay + run_app + + stop_lttng_tracing $SESSION_NAME + destroy_lttng_session $SESSION_NAME + + # Validate test. Expecting 0 events. + trace_match_only $EVENT_NAME 0 $TRACE_PATH + if [ $? -ne 0 ]; then + return $? + fi + + diag "Test Python applications with higher loglevel" + + create_lttng_session $SESSION_NAME $TRACE_PATH + enable_python_lttng_event_loglevel $SESSION_NAME $EVENT_NAME "DEBUG" + start_lttng_tracing $SESSION_NAME + + # Run 5 times with a 1 second delay + run_app + + stop_lttng_tracing $SESSION_NAME + destroy_lttng_session $SESSION_NAME + + # Validate test. Expecting all events. + trace_match_only $EVENT_NAME $NR_ITER $TRACE_PATH + return $? +} + +function test_python_loglevel_multiple () +{ + diag "Test Python application with multiple loglevel" + + create_lttng_session $SESSION_NAME $TRACE_PATH + enable_python_lttng_event_loglevel $SESSION_NAME $EVENT_NAME "INFO" + enable_python_lttng_event_loglevel $SESSION_NAME $EVENT_NAME "DEBUG" + start_lttng_tracing $SESSION_NAME + + # Run 5 times with a 1 second delay and fire two TP. + run_app 1 + + stop_lttng_tracing $SESSION_NAME + destroy_lttng_session $SESSION_NAME + + # Validate test. Expecting all events times two. + trace_match_only $EVENT_NAME $(($NR_ITER * 2)) $TRACE_PATH + if [ $? -ne 0 ]; then + return $? + fi + + create_lttng_session $SESSION_NAME $TRACE_PATH + enable_python_lttng_event_loglevel $SESSION_NAME '*' "INFO" + enable_python_lttng_event_loglevel $SESSION_NAME '*' "DEBUG" + start_lttng_tracing $SESSION_NAME + + # Run 5 times with a 1 second delay and fire two TP. + run_app 1 + + stop_lttng_tracing $SESSION_NAME + destroy_lttng_session $SESSION_NAME + + # Validate test. Expecting all events times two. + trace_match_only $EVENT_NAME $(($NR_ITER * 2)) $TRACE_PATH + if [ $? -ne 0 ]; then + return $? + fi +} + +function test_python_multi_session_loglevel() +{ + diag "Test Python with multiple session" + + create_lttng_session $SESSION_NAME-1 $TRACE_PATH/$SESSION_NAME-1 + enable_python_loglevel_only $SESSION_NAME-1 '*' "INFO" + start_lttng_tracing $SESSION_NAME-1 + + create_lttng_session $SESSION_NAME-2 $TRACE_PATH/$SESSION_NAME-2 + enable_python_loglevel_only $SESSION_NAME-2 '*' "DEBUG" + start_lttng_tracing $SESSION_NAME-2 + + # Run 5 times with a 1 second delay and fire second TP. + run_app 1 1 + + stop_lttng_tracing $SESSION_NAME-1 + stop_lttng_tracing $SESSION_NAME-2 + destroy_lttng_session $SESSION_NAME-1 + destroy_lttng_session $SESSION_NAME-2 + + # Expecting NR_ITER events being the main event and the second tp one. + trace_matches $EVENT_NAME $NR_ITER $TRACE_PATH/$SESSION_NAME-1 + if [ $? -ne 0 ]; then + return $? + fi + trace_matches $EVENT_NAME2 1 $TRACE_PATH/$SESSION_NAME-1 + if [ $? -ne 0 ]; then + return $? + fi + + # Expectin NR_ITER events being the debug TP. + trace_match_only $EVENT_NAME $NR_ITER $TRACE_PATH/$SESSION_NAME-2 + if [ $? -ne 0 ]; then + return $? + fi +} + +function test_python_multi_session_disable() +{ + diag "Test Python with multiple session with disabled event" + + create_lttng_session $SESSION_NAME-1 $TRACE_PATH/$SESSION_NAME-1 + enable_python_lttng_event $SESSION_NAME-1 $EVENT_NAME + enable_python_lttng_event $SESSION_NAME-1 $EVENT_NAME2 + disable_python_lttng_event $SESSION_NAME-1 $EVENT_NAME + start_lttng_tracing $SESSION_NAME-1 + + create_lttng_session $SESSION_NAME-2 $TRACE_PATH/$SESSION_NAME-2 + enable_python_lttng_event $SESSION_NAME-2 $EVENT_NAME2 + start_lttng_tracing $SESSION_NAME-2 + + # Run 5 times with a 1 second delay and fire second TP. + run_app 0 1 + + stop_lttng_tracing $SESSION_NAME-1 + stop_lttng_tracing $SESSION_NAME-2 + destroy_lttng_session $SESSION_NAME-1 + destroy_lttng_session $SESSION_NAME-2 + + # Validate test. Expecting one event of the second TP. + trace_match_only $EVENT_NAME2 1 $TRACE_PATH/$SESSION_NAME-1 + if [ $? -ne 0 ]; then + return $? + fi + + # Validate test. Expecting one event of the second TP. + trace_match_only $EVENT_NAME2 1 $TRACE_PATH/$SESSION_NAME-2 + if [ $? -ne 0 ]; then + return $? + fi +} + +function test_python_multi_session_disable_wildcard() +{ + diag "Test Python with multiple session with disabled wildcard event" + + create_lttng_session $SESSION_NAME-1 $TRACE_PATH/$SESSION_NAME-1 + enable_python_lttng_event $SESSION_NAME-1 '*' + + create_lttng_session $SESSION_NAME-2 $TRACE_PATH/$SESSION_NAME-2 + enable_python_lttng_event $SESSION_NAME-2 '*' + + disable_python_lttng_event $SESSION_NAME-1 '*' + + start_lttng_tracing $SESSION_NAME-1 + start_lttng_tracing $SESSION_NAME-2 + + run_app + + stop_lttng_tracing $SESSION_NAME-1 + stop_lttng_tracing $SESSION_NAME-2 + destroy_lttng_session $SESSION_NAME-1 + destroy_lttng_session $SESSION_NAME-2 + + # Validate test. Expecting NO event of the first TP. + trace_match_only $EVENT_NAME 0 $TRACE_PATH/$SESSION_NAME-1 + if [ $? -ne 0 ]; then + return $? + fi + + # Validate test. Expecting all events of the first TP. + trace_match_only $EVENT_NAME $NR_ITER $TRACE_PATH/$SESSION_NAME-2 + if [ $? -ne 0 ]; then + return $? + fi +} + +function test_python_disable_all() +{ + diag "Test Python with multiple session with disabled all event" + + create_lttng_session $SESSION_NAME $TRACE_PATH/$SESSION_NAME + enable_python_lttng_event $SESSION_NAME '*' + enable_python_lttng_event $SESSION_NAME $EVENT_NAME + enable_python_lttng_event $SESSION_NAME $EVENT_NAME2 + + disable_python_lttng_event $SESSION_NAME '*' + + start_lttng_tracing $SESSION_NAME + + run_app 0 1 + + stop_lttng_tracing $SESSION_NAME + destroy_lttng_session $SESSION_NAME + + # Validate test. Expecting NO event of the first TP and second TP. + trace_match_only $EVENT_NAME 0 $TRACE_PATH/$SESSION_NAME + trace_match_only $EVENT_NAME2 0 $TRACE_PATH/$SESSION_NAME + if [ $? -ne 0 ]; then + return $? + fi +} + +function test_python_multi_session() +{ + diag "Test Python with multiple session" + + create_lttng_session $SESSION_NAME-1 $TRACE_PATH/$SESSION_NAME-1 + enable_python_lttng_event $SESSION_NAME-1 $EVENT_NAME + start_lttng_tracing $SESSION_NAME-1 + + create_lttng_session $SESSION_NAME-2 $TRACE_PATH/$SESSION_NAME-2 + enable_python_lttng_event $SESSION_NAME-2 $EVENT_NAME2 + start_lttng_tracing $SESSION_NAME-2 + + # Run 5 times with a 1 second delay and fire second TP. + run_app 0 1 + + stop_lttng_tracing $SESSION_NAME-1 + stop_lttng_tracing $SESSION_NAME-2 + destroy_lttng_session $SESSION_NAME-1 + destroy_lttng_session $SESSION_NAME-2 + + # Validate test. Expecting all events of first TP + trace_match_only $EVENT_NAME $NR_ITER $TRACE_PATH/$SESSION_NAME-1 + if [ $? -ne 0 ]; then + return $? + fi + + # Validate test. Expecting one event of the second TP. + trace_match_only $EVENT_NAME2 1 $TRACE_PATH/$SESSION_NAME-2 + if [ $? -ne 0 ]; then + return $? + fi +} + +function test_python_destroy_session() +{ + diag "Test Python two session with destroy" + + create_lttng_session $SESSION_NAME $TRACE_PATH/first-sess + enable_python_lttng_event $SESSION_NAME $EVENT_NAME + start_lttng_tracing $SESSION_NAME + + # Run 5 times with a 1 second delay + run_app_background 0 1 + + sleep 1 + + stop_lttng_tracing $SESSION_NAME + destroy_lttng_session $SESSION_NAME + + # Validate test. Expecting at least one event num 1 + validate_trace $EVENT_NAME $TRACE_PATH/first-sess + if [ $? -ne 0 ]; then + return $? + fi + + create_lttng_session $SESSION_NAME $TRACE_PATH/second-sess + enable_python_lttng_event $SESSION_NAME $EVENT_NAME2 + start_lttng_tracing $SESSION_NAME + + # Wait for the applications started in background + wait ${!} + + stop_lttng_tracing $SESSION_NAME + destroy_lttng_session $SESSION_NAME + + # Validate test. Expecting only one event num 2 + trace_match_only $EVENT_NAME2 1 $TRACE_PATH/second-sess + if [ $? -ne 0 ]; then + return $? + fi +} + +function test_python_filtering() +{ + diag "Test Python filtering" + + create_lttng_session $SESSION_NAME $TRACE_PATH/$SESSION_NAME + # Enable all event with a filter. + enable_python_filter $SESSION_NAME '*' 'msg == "python-ev-test2 fired"' + start_lttng_tracing $SESSION_NAME + + # Run 5 times with a 1 second delay and fire second TP. + run_app 0 1 + + stop_lttng_tracing $SESSION_NAME + destroy_lttng_session $SESSION_NAME + + # Validate test. Expecting one event of the second TP only. + trace_match_only $EVENT_NAME2 1 $TRACE_PATH/$SESSION_NAME + if [ $? -ne 0 ]; then + return $? + fi + + create_lttng_session $SESSION_NAME $TRACE_PATH/$SESSION_NAME + # Enable first Logger but filter msg payload for the INFO one while + # triggering the debug and second TP. + enable_python_filter $SESSION_NAME $EVENT_NAME 'msg == "python-ev-test1 fired"' + start_lttng_tracing $SESSION_NAME + + # Run 5 times with a 1 second delay, fire debug and second TP. + run_app 1 1 + + stop_lttng_tracing $SESSION_NAME + destroy_lttng_session $SESSION_NAME + + # Validate test. Expecting NR_ITER event of the main INFO tp. + trace_match_only $EVENT_NAME $NR_ITER $TRACE_PATH/$SESSION_NAME + if [ $? -ne 0 ]; then + return $? + fi +} + +function test_python_disable() +{ + diag "Test Python disable event" + + create_lttng_session $SESSION_NAME $TRACE_PATH/$SESSION_NAME + # Enable all event with a filter. + enable_python_lttng_event $SESSION_NAME $EVENT_NAME + enable_python_lttng_event $SESSION_NAME $EVENT_NAME2 + disable_python_lttng_event $SESSION_NAME $EVENT_NAME + start_lttng_tracing $SESSION_NAME + + # Run 5 times with a 1 second delay and fire second TP. + run_app 0 1 + + stop_lttng_tracing $SESSION_NAME + destroy_lttng_session $SESSION_NAME + + # Validate test. Expecting one event of the second TP only. + trace_match_only $EVENT_NAME2 1 $TRACE_PATH/$SESSION_NAME + if [ $? -ne 0 ]; then + return $? + fi +} + +function test_python_disable_enable() +{ + diag "Test Python disable event followed by an enable" + + create_lttng_session $SESSION_NAME $TRACE_PATH/$SESSION_NAME + # Enable all event with a filter. + enable_python_lttng_event $SESSION_NAME $EVENT_NAME + disable_python_lttng_event $SESSION_NAME $EVENT_NAME + enable_python_lttng_event $SESSION_NAME $EVENT_NAME + start_lttng_tracing $SESSION_NAME + + # Run 5 times with a 1 second delay and fire second TP. + run_app 0 1 + + stop_lttng_tracing $SESSION_NAME + destroy_lttng_session $SESSION_NAME + + # Validate test. Expecting NR_ITER event of the main INFO tp. + trace_match_only $EVENT_NAME $NR_ITER $TRACE_PATH/$SESSION_NAME + if [ $? -ne 0 ]; then + return $? + fi +} + +function test_python_filter_loglevel() +{ + local BOGUS_EVENT_NAME="not_a_real_event" + local FILTER="int_loglevel > 30 || int_loglevel < 30" + local ALL_EVENTS="." + + diag "Test Python a filter with a loglevel" + + create_lttng_session $SESSION_NAME $TRACE_PATH/$SESSION_NAME + # Enable an event with a filter and the loglevel-only option. + enable_python_filter_loglevel_only $SESSION_NAME $BOGUS_EVENT_NAME "$FILTER" "INFO" + disable_python_lttng_event $SESSION_NAME $BOGUS_EVENT_NAME + enable_python_filter_loglevel_only $SESSION_NAME $BOGUS_EVENT_NAME "$FILTER" "INFO" + start_lttng_tracing $SESSION_NAME + + # Run 5 times with a 1 second delay and fire second TP. + run_app 0 1 + + stop_lttng_tracing $SESSION_NAME + destroy_lttng_session $SESSION_NAME + + # Validate test. Expecting no events. + trace_match_only $ALL_EVENTS 0 $TRACE_PATH/$SESSION_NAME + if [ $? -ne 0 ]; then + return $? + fi +} + +plan_tests $NUM_TESTS + +print_test_banner "$TEST_DESC" + +if [ ! -f "$TESTAPP_BIN" ]; then + withapp=0 +else + withapp=1 +fi + +skip $withapp "Python support is needed. Skipping all tests." $NUM_TESTS || +{ + start_lttng_sessiond + + tests=( + test_python_multi_session_disable_wildcard + test_python_multi_session_disable + test_python_disable + test_python_disable_enable + test_python_disable_all + test_python_filtering + test_python_multi_session_loglevel + test_python_destroy_session + test_python_loglevel + test_python_loglevel_multiple + test_python_before_start + test_python_after_start + test_python_multi_session + test_python_filter_loglevel + ) + + for fct_test in ${tests[@]}; + do + TRACE_PATH=$(mktemp -d) + + ${fct_test} + if [ $? -ne 0 ]; then + break; + fi + rm -rf $TRACE_PATH + done + + stop_lttng_sessiond +} diff --git a/tests/utils/utils.sh b/tests/utils/utils.sh index e2cd7f881..fe75461fc 100644 --- a/tests/utils/utils.sh +++ b/tests/utils/utils.sh @@ -514,6 +514,41 @@ function enable_log4j_lttng_event_loglevel() ok $? "Enable LOG4J event $event_name for session $sess_name with loglevel $loglevel" } +function enable_python_lttng_event() +{ + sess_name=$1 + event_name="$2" + channel_name=$3 + + if [ -z $channel_name ]; then + # default channel if none specified + chan="" + else + chan="-c $channel_name" + fi + + $TESTDIR/../src/bin/lttng/$LTTNG_BIN enable-event "$event_name" $chan -s $sess_name -p >$OUTPUT_DEST + ok $? "Enable Python event $event_name for session $sess_name" +} + +function enable_python_lttng_event_loglevel() +{ + local sess_name=$1 + local event_name="$2" + local loglevel=$3 + local channel_name=$4 + + if [ -z $channel_name ]; then + # default channel if none specified + chan="" + else + chan="-c $channel_name" + fi + + $TESTDIR/../src/bin/lttng/$LTTNG_BIN enable-event --loglevel $loglevel "$event_name" $chan -s $sess_name -p >$OUTPUT_DEST + ok $? "Enable Python event $event_name for session $sess_name with loglevel $loglevel" +} + function enable_ust_lttng_event_filter() { local sess_name="$1" @@ -579,6 +614,15 @@ function disable_log4j_lttng_event () ok $? "Disable LOG4J event $event_name for session $sess_name" } +function disable_python_lttng_event () +{ + local sess_name="$1" + local event_name="$2" + + $TESTDIR/../src/bin/lttng/$LTTNG_BIN disable-event "$event_name" -s $sess_name -p >$OUTPUT_DEST + ok $? "Disable Python event $event_name for session $sess_name" +} + function start_lttng_tracing () { local sess_name=$1 -- 2.34.1