From: Mathieu Desnoyers Date: Tue, 8 Jul 2014 14:12:23 +0000 (-0400) Subject: Implement --shm-path option for UST sessions (per-uid channels) X-Git-Url: https://git.lttng.org/?p=lttng-tools.git;a=commitdiff_plain;h=d7ba13889c8692b14f99238ddf2721ed78df89d2 Implement --shm-path option for UST sessions (per-uid channels) Signed-off-by: Mathieu Desnoyers Signed-off-by: Jérémie Galarneau --- diff --git a/.gitignore b/.gitignore index a5d110b8a..ff19c8d0d 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,7 @@ tags /src/bin/lttng-sessiond/lttng-sessiond /src/bin/lttng/lttng /src/bin/lttng-consumerd/lttng-consumerd +/src/bin/lttng-crash/lttng-crash /src/bin/lttng-relayd/lttng-relayd /src/lib/lttng-ctl/lttng-ctl.pc /src/lib/lttng-ctl/filter/filter-grammar-test diff --git a/configure.ac b/configure.ac index b664641be..75fe1e074 100644 --- a/configure.ac +++ b/configure.ac @@ -496,6 +496,7 @@ AC_CONFIG_FILES([ src/bin/lttng-sessiond/Makefile src/bin/lttng-relayd/Makefile src/bin/lttng/Makefile + src/bin/lttng-crash/Makefile tests/Makefile tests/regression/Makefile tests/regression/kernel/Makefile diff --git a/doc/man/lttng.1 b/doc/man/lttng.1 index fd62558ee..9f642b812 100644 --- a/doc/man/lttng.1 +++ b/doc/man/lttng.1 @@ -302,6 +302,12 @@ $ lttng start After the start, you'll be able to read the events while they are being recorded in /tmp/lttng. +.TP +.BR "\-\-shm-path PATH" + +Path where shared memory holding buffers should be created. Useful +when used with pramfs to extract trace data after crash. + .TP .BR "\-U, \-\-set-url=URL" Set URL for the consumer output destination. It is persistent for the diff --git a/include/lttng/session.h b/include/lttng/session.h index 2c9b842d4..c87efbd1d 100644 --- a/include/lttng/session.h +++ b/include/lttng/session.h @@ -104,6 +104,18 @@ extern int lttng_destroy_session(const char *name); */ extern int lttng_list_sessions(struct lttng_session **sessions); +/* + * Set the shared memory path for a session. + * + * Sets the (optional) file system path where shared memory buffers will + * be created for the session. This is useful for buffer extraction on + * crash, when used with filesystems like pramfs. + * + * Return 0 on success else a negative LTTng error code. + */ +extern int lttng_set_session_shm_path(const char *session_name, + const char *shm_path); + #ifdef __cplusplus } #endif diff --git a/src/bin/Makefile.am b/src/bin/Makefile.am index c97d1a147..ebf9b57ff 100644 --- a/src/bin/Makefile.am +++ b/src/bin/Makefile.am @@ -3,4 +3,5 @@ ACLOCAL_AMFLAGS = -I config SUBDIRS = lttng-consumerd \ lttng \ lttng-sessiond \ - lttng-relayd + lttng-relayd \ + lttng-crash diff --git a/src/bin/lttng-crash/Makefile.am b/src/bin/lttng-crash/Makefile.am new file mode 100644 index 000000000..a0adfbc84 --- /dev/null +++ b/src/bin/lttng-crash/Makefile.am @@ -0,0 +1,9 @@ +AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src \ + -DINSTALL_BIN_PATH=\""$(bindir)"\" + +bin_PROGRAMS = lttng-crash + +lttng_crash_SOURCES = lttng-crash.c + +lttng_crash_LDADD = $(top_builddir)/src/common/libcommon.la \ + $(top_builddir)/src/common/config/libconfig.la diff --git a/src/bin/lttng-crash/lttng-crash.c b/src/bin/lttng-crash/lttng-crash.c new file mode 100644 index 000000000..dda157be3 --- /dev/null +++ b/src/bin/lttng-crash/lttng-crash.c @@ -0,0 +1,1109 @@ +/* + * Copyright (C) 2011 David Goulet + * Copyright (C) 2014 Mathieu Desnoyers + * + * 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. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define DEFAULT_VIEWER "babeltrace" + +#define COPY_BUFLEN 4096 +#define RB_CRASH_DUMP_ABI_LEN 32 + +#define RB_CRASH_DUMP_ABI_MAGIC_LEN 16 + +/* + * The 128-bit magic number is xor'd in the process data so it does not + * cause a false positive when searching for buffers by scanning memory. + * The actual magic number is: + * 0x17, 0x7B, 0xF1, 0x77, 0xBF, 0x17, 0x7B, 0xF1, + * 0x77, 0xBF, 0x17, 0x7B, 0xF1, 0x77, 0xBF, 0x17, + */ +#define RB_CRASH_DUMP_ABI_MAGIC_XOR \ + { \ + 0x17 ^ 0xFF, 0x7B ^ 0xFF, 0xF1 ^ 0xFF, 0x77 ^ 0xFF, \ + 0xBF ^ 0xFF, 0x17 ^ 0xFF, 0x7B ^ 0xFF, 0xF1 ^ 0xFF, \ + 0x77 ^ 0xFF, 0xBF ^ 0xFF, 0x17 ^ 0xFF, 0x7B ^ 0xFF, \ + 0xF1 ^ 0xFF, 0x77 ^ 0xFF, 0xBF ^ 0xFF, 0x17 ^ 0xFF, \ + } + +/* + * Non-static to ensure the compiler does not optimize away the xor. + */ +uint8_t lttng_crash_expected_magic_xor[] = RB_CRASH_DUMP_ABI_MAGIC_XOR; + +#define RB_CRASH_ENDIAN 0x1234 +#define RB_CRASH_ENDIAN_REVERSE 0x3412 + +enum lttng_crash_type { + LTTNG_CRASH_TYPE_UST = 0, + LTTNG_CRASH_TYPE_KERNEL = 1, +}; + +/* LTTng ring buffer defines (copied) */ + +#define HALF_ULONG_BITS(wl) (((wl) * CHAR_BIT) >> 1) + +#define SB_ID_OFFSET_SHIFT(wl) (HALF_ULONG_BITS(wl) + 1) +#define SB_ID_OFFSET_COUNT(wl) (1UL << SB_ID_OFFSET_SHIFT(wl)) +#define SB_ID_OFFSET_MASK(wl) (~(SB_ID_OFFSET_COUNT(wl) - 1)) +/* + * Lowest bit of top word half belongs to noref. Used only for overwrite mode. + */ +#define SB_ID_NOREF_SHIFT(wl) (SB_ID_OFFSET_SHIFT(wl) - 1) +#define SB_ID_NOREF_COUNT(wl) (1UL << SB_ID_NOREF_SHIFT(wl)) +#define SB_ID_NOREF_MASK(wl) SB_ID_NOREF_COUNT(wl) +/* + * In overwrite mode: lowest half of word is used for index. + * Limit of 2^16 subbuffers per buffer on 32-bit, 2^32 on 64-bit. + * In producer-consumer mode: whole word used for index. + */ +#define SB_ID_INDEX_SHIFT(wl) 0 +#define SB_ID_INDEX_COUNT(wl) (1UL << SB_ID_INDEX_SHIFT(wl)) +#define SB_ID_INDEX_MASK(wl) (SB_ID_NOREF_COUNT(wl) - 1) + +enum rb_modes { + RING_BUFFER_OVERWRITE = 0, /* Overwrite when buffer full */ + RING_BUFFER_DISCARD = 1, /* Discard when buffer full */ +}; + +struct crash_abi_unknown { + uint8_t magic[RB_CRASH_DUMP_ABI_MAGIC_LEN]; + uint64_t mmap_length; /* Overall lenght of crash record */ + uint16_t endian; /* + * { 0x12, 0x34 }: big endian + * { 0x34, 0x12 }: little endian + */ + uint16_t major; /* Major number. */ + uint16_t minor; /* Minor number. */ + uint8_t word_size; /* Word size (bytes). */ + uint8_t layout_type; /* enum lttng_crash_layout */ +} __attribute__((packed)); + +struct crash_abi_0_0 { + struct crash_abi_unknown parent; + + struct { + uint32_t prod_offset; + uint32_t consumed_offset; + uint32_t commit_hot_array; + uint32_t commit_hot_seq; + uint32_t buf_wsb_array; + uint32_t buf_wsb_id; + uint32_t sb_array; + uint32_t sb_array_shmp_offset; + uint32_t sb_backend_p_offset; + uint32_t content_size; + uint32_t packet_size; + } __attribute__((packed)) offset; + struct { + uint8_t prod_offset; + uint8_t consumed_offset; + uint8_t commit_hot_seq; + uint8_t buf_wsb_id; + uint8_t sb_array_shmp_offset; + uint8_t sb_backend_p_offset; + uint8_t content_size; + uint8_t packet_size; + } __attribute__((packed)) length; + struct { + uint32_t commit_hot_array; + uint32_t buf_wsb_array; + uint32_t sb_array; + } __attribute__((packed)) stride; + + uint64_t buf_size; /* Size of the buffer */ + uint64_t subbuf_size; /* Sub-buffer size */ + uint64_t num_subbuf; /* Number of sub-buffers for writer */ + uint32_t mode; /* Buffer mode: 0: overwrite, 1: discard */ +} __attribute__((packed)); + +struct lttng_crash_layout { + struct { + int prod_offset, consumed_offset, + commit_hot_array, commit_hot_seq, + buf_wsb_array, buf_wsb_id, + sb_array, sb_array_shmp_offset, + sb_backend_p_offset, content_size, + packet_size; + } offset; + struct { + int prod_offset, consumed_offset, + commit_hot_seq, buf_wsb_id, + sb_array_shmp_offset, sb_backend_p_offset, + content_size, packet_size; + } length; + struct { + int commit_hot_array, buf_wsb_array, sb_array; + } stride; + + int reverse_byte_order; + int word_size; + + uint64_t mmap_length; /* Length of crash record */ + uint64_t buf_size; /* Size of the buffer */ + uint64_t subbuf_size; /* Sub-buffer size */ + uint64_t num_subbuf; /* Number of sub-buffers for writer */ + uint32_t mode; /* Buffer mode: 0: overwrite, 1: discard */ +}; + +/* Variables */ +static char *progname, + *opt_viewer_path = DEFAULT_VIEWER, + *opt_output_path; + +static int nr_input_paths; +static char **input_paths; + +int lttng_opt_quiet, lttng_opt_verbose, lttng_opt_mi; + +enum { + OPT_DUMP_OPTIONS, +}; + +/* Getopt options. No first level command. */ +static struct option long_options[] = { + { "version", 0, NULL, 'V' }, + { "help", 0, NULL, 'h' }, + { "verbose", 0, NULL, 'v' }, + { "viewer", 1, NULL, 'e' }, + { "extract", 1, NULL, 'x' }, + { "list-options", 0, NULL, OPT_DUMP_OPTIONS }, + { NULL, 0, NULL, 0 }, +}; + +static void usage(FILE *ofp) +{ + fprintf(ofp, "LTTng Crash Trace Viewer " VERSION " - " VERSION_NAME "%s\n\n", + GIT_VERSION[0] == '\0' ? "" : " - " GIT_VERSION); + fprintf(ofp, "usage: lttng [OPTIONS] FILE...\n"); + fprintf(ofp, "\n"); + fprintf(ofp, "Options:\n"); + fprintf(ofp, " -V, --version Show version.\n"); + fprintf(ofp, " -h, --help Show this help.\n"); + fprintf(ofp, " --list-options Simple listing of lttng-crash options.\n"); + fprintf(ofp, " -v, --verbose Increase verbosity.\n"); + fprintf(ofp, " -e, --viewer Specify viewer and/or options to use. This will\n" + " completely override the default viewers so please\n" + " make sure to specify the full command. The trace\n" + " directory paths appended at the end to the\n" + " arguments.\n"); + fprintf(ofp, " -x, --extract PATH Extract trace(s) to specified path. Don't view\n" + " trace.\n"); + fprintf(ofp, "\n"); + fprintf(ofp, "Please see the lttng-crash(1) man page for full documentation.\n"); + fprintf(ofp, "See http://lttng.org for updates, bug reports and news.\n"); +} + +static void version(FILE *ofp) +{ + fprintf(ofp, "%s (LTTng Crash Trace Viewer) " VERSION " - " VERSION_NAME +"%s\n", + progname, + GIT_VERSION[0] == '\0' ? "" : " - " GIT_VERSION); +} + +/* + * list_options + * + * List options line by line. This is mostly for bash auto completion and to + * avoid difficult parsing. + */ +static void list_options(FILE *ofp) +{ + int i = 0; + struct option *option = NULL; + + option = &long_options[i]; + while (option->name != NULL) { + fprintf(ofp, "--%s\n", option->name); + + if (isprint(option->val)) { + fprintf(ofp, "-%c\n", option->val); + } + + i++; + option = &long_options[i]; + } +} + +/* + * Parse command line arguments. + * + * Return 0 if OK, else -1 + */ +static int parse_args(int argc, char **argv) +{ + int opt, ret = 0; + + if (argc < 2) { + usage(stderr); + exit(EXIT_FAILURE); + } + + while ((opt = getopt_long(argc, argv, "+Vhvex:", long_options, NULL)) != -1) { + switch (opt) { + case 'V': + version(stdout); + ret = 1; + goto end; + case 'h': + usage(stdout); + ret = 1; + goto end; + case 'v': + /* There is only 3 possible level of verbosity. (-vvv) */ + if (lttng_opt_verbose < 3) { + lttng_opt_verbose += 1; + } + break; + case 'e': + opt_viewer_path = strdup(optarg); + break; + case 'x': + opt_output_path = strdup(optarg); + break; + case OPT_DUMP_OPTIONS: + list_options(stdout); + ret = 1; + goto end; + default: + usage(stderr); + goto error; + } + } + + /* No leftovers, print usage and quit */ + if ((argc - optind) == 0) { + usage(stderr); + goto error; + } + + input_paths = &argv[optind]; + nr_input_paths = argc - optind; +end: + return ret; + +error: + return -1; +} + +static +int copy_file(const char *file_dest, const char *file_src) +{ + int fd_src, fd_dest; + ssize_t readlen, writelen; + char buf[COPY_BUFLEN]; + + DBG("Copy metadata file '%s' into '%s'", file_src, file_dest); + + fd_src = open(file_src, O_RDONLY); + if (fd_src < 0) { + PERROR("Error opening %s for reading", file_src); + return fd_src; + } + fd_dest = open(file_dest, O_RDWR | O_CREAT | O_EXCL, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + if (fd_dest < 0) { + PERROR("Error opening %s for writing", file_dest); + return fd_dest; + } + + for (;;) { + readlen = lttng_read(fd_src, buf, COPY_BUFLEN); + if (readlen < 0) { + PERROR("Error reading input file"); + return -1; + } + if (!readlen) { + break; + } + writelen = lttng_write(fd_dest, buf, readlen); + if (writelen < readlen) { + PERROR("Error writing to output file"); + return -1; + } + } + return 0; +} + +static +uint64_t _crash_get_field(const struct lttng_crash_layout *layout, + const char *ptr, size_t size) +{ + switch (size) { + case 1: return *(uint8_t *) ptr; + case 2: if (layout->reverse_byte_order) { + return __bswap_16(*(uint16_t *) ptr); + } else { + return *(uint16_t *) ptr; + + } + case 4: if (layout->reverse_byte_order) { + return __bswap_32(*(uint32_t *) ptr); + } else { + return *(uint32_t *) ptr; + + } + case 8: if (layout->reverse_byte_order) { + return __bswap_64(*(uint64_t *) ptr); + } else { + return *(uint64_t *) ptr; + } + default: + abort(); + return -1; + } + +} + +#define crash_get_field(layout, map, name) \ + _crash_get_field(layout, (map) + (layout)->offset.name, \ + layout->length.name) + +#define crash_get_array_field(layout, map, array_name, idx, field_name) \ + _crash_get_field(layout, \ + (map) + (layout)->offset.array_name \ + + (idx * (layout)->stride.array_name) \ + + (layout)->offset.field_name, \ + (layout)->length.field_name) + +#define crash_get_hdr_raw_field(layout, hdr, name) ((hdr)->name) + +#define crash_get_hdr_field(layout, hdr, name) \ + _crash_get_field(layout, (const char *) &(hdr)->name, \ + sizeof((hdr)->name)) + +#define crash_get_layout(layout, hdr, name) \ + do { \ + (layout)->name = crash_get_hdr_field(layout, hdr, \ + name); \ + DBG("layout.%s = %" PRIu64, #name, \ + (uint64_t) (layout)->name); \ + } while (0) + +static +int get_crash_layout_0_0(struct lttng_crash_layout *layout, + char *map) +{ + const struct crash_abi_0_0 *abi = (const struct crash_abi_0_0 *) map; + + crash_get_layout(layout, abi, offset.prod_offset); + crash_get_layout(layout, abi, offset.consumed_offset); + crash_get_layout(layout, abi, offset.commit_hot_array); + crash_get_layout(layout, abi, offset.commit_hot_seq); + crash_get_layout(layout, abi, offset.buf_wsb_array); + crash_get_layout(layout, abi, offset.buf_wsb_id); + crash_get_layout(layout, abi, offset.sb_array); + crash_get_layout(layout, abi, offset.sb_array_shmp_offset); + crash_get_layout(layout, abi, offset.sb_backend_p_offset); + crash_get_layout(layout, abi, offset.content_size); + crash_get_layout(layout, abi, offset.packet_size); + + crash_get_layout(layout, abi, length.prod_offset); + crash_get_layout(layout, abi, length.consumed_offset); + crash_get_layout(layout, abi, length.commit_hot_seq); + crash_get_layout(layout, abi, length.buf_wsb_id); + crash_get_layout(layout, abi, length.sb_array_shmp_offset); + crash_get_layout(layout, abi, length.sb_backend_p_offset); + crash_get_layout(layout, abi, length.content_size); + crash_get_layout(layout, abi, length.packet_size); + + crash_get_layout(layout, abi, stride.commit_hot_array); + crash_get_layout(layout, abi, stride.buf_wsb_array); + crash_get_layout(layout, abi, stride.sb_array); + + crash_get_layout(layout, abi, buf_size); + crash_get_layout(layout, abi, subbuf_size); + crash_get_layout(layout, abi, num_subbuf); + crash_get_layout(layout, abi, mode); + + return 0; +} + +static +void print_dbg_magic(const uint8_t *magic) +{ + DBG("magic: 0x%X%X%X%X%X%X%X%X%X%X%X%X%X%X%X%X", + magic[0], magic[1], magic[2], magic[3], + magic[4], magic[5], magic[6], magic[7], + magic[8], magic[9], magic[10], magic[11], + magic[12], magic[13], magic[14], magic[15]); +} + +static +int check_magic(const uint8_t *magic) +{ + int i; + + for (i = 0; i < RB_CRASH_DUMP_ABI_MAGIC_LEN; i++) { + if ((magic[i] ^ 0xFF) != lttng_crash_expected_magic_xor[i]) { + return -1; + } + } + return 0; +} + +static +int get_crash_layout(struct lttng_crash_layout *layout, int fd) +{ + char *map; + int ret = 0, unmapret; + const uint8_t *magic; + uint64_t mmap_length; + uint16_t major, minor; + uint8_t word_size; + const struct crash_abi_unknown *abi; + uint16_t endian; + enum lttng_crash_type layout_type; + + map = mmap(NULL, RB_CRASH_DUMP_ABI_LEN, PROT_READ, MAP_PRIVATE, + fd, 0); + if (map == MAP_FAILED) { + PERROR("Mapping file"); + return -1; + } + abi = (const struct crash_abi_unknown *) map; + magic = crash_get_hdr_raw_field(layout, abi, magic); + print_dbg_magic(magic); + if (check_magic(magic)) { + DBG("Unknown magic number"); + ret = 1; /* positive return value, skip */ + goto end; + } + endian = crash_get_hdr_field(layout, abi, endian); + switch (endian) { + case RB_CRASH_ENDIAN: + break; + case RB_CRASH_ENDIAN_REVERSE: + layout->reverse_byte_order = 1; + break; + default: + DBG("Unknown endianness value: 0x%X", (unsigned int) endian); + ret = 1; /* positive return value, skip */ + goto end; + } + layout_type = (enum lttng_crash_type) crash_get_hdr_field(layout, abi, layout_type); + switch (layout_type) { + case LTTNG_CRASH_TYPE_UST: + break; + case LTTNG_CRASH_TYPE_KERNEL: + ERR("lttng-modules buffer layout support not implemented"); + ret = 1; /* positive return value, skip */ + goto end; + default: + ERR("Unknown layout type %u", (unsigned int) layout_type); + ret = 1; /* positive return value, skip */ + goto end; + } + mmap_length = crash_get_hdr_field(layout, abi, mmap_length); + DBG("mmap_length: %" PRIu64, mmap_length); + layout->mmap_length = mmap_length; + major = crash_get_hdr_field(layout, abi, major); + DBG("major: %u", major); + minor = crash_get_hdr_field(layout, abi, minor); + DBG("minor: %u", minor); + word_size = crash_get_hdr_field(layout, abi, word_size); + DBG("word_size: %u", word_size); + switch (major) { + case 0: + switch (minor) { + case 0: + ret = get_crash_layout_0_0(layout, map); + if (ret) + goto end; + break; + default: + ret = -1; + ERR("Unsupported crash ABI %u.%u\n", major, minor); + goto end; + } + break; + default: + ERR("Unsupported crash ABI %u.%u\n", major, minor); + ret = -1; + goto end; + } + layout->word_size = word_size; +end: + unmapret = munmap(map, RB_CRASH_DUMP_ABI_LEN); + if (unmapret) { + PERROR("munmap"); + } + return ret; +} + +/* buf_trunc mask selects only the buffer number. */ +static inline +uint64_t buf_trunc(uint64_t offset, uint64_t buf_size) +{ + return offset & ~(buf_size - 1); +} + +/* subbuf_trunc mask selects the subbuffer number. */ +static inline +uint64_t subbuf_trunc(uint64_t offset, uint64_t subbuf_size) +{ + return offset & ~(subbuf_size - 1); +} + +/* buf_offset mask selects only the offset within the current buffer. */ +static inline +uint64_t buf_offset(uint64_t offset, uint64_t buf_size) +{ + return offset & (buf_size - 1); +} + +/* subbuf_offset mask selects the offset within the current subbuffer. */ +static inline +uint64_t subbuf_offset(uint64_t offset, uint64_t subbuf_size) +{ + return offset & (subbuf_size - 1); +} + +/* subbuf_index returns the index of the current subbuffer within the buffer. */ +static inline +uint64_t subbuf_index(uint64_t offset, uint64_t buf_size, uint64_t subbuf_size) +{ + return buf_offset(offset, buf_size) / subbuf_size; +} + +static inline +uint64_t subbuffer_id_get_index(uint32_t mode, uint64_t id, + unsigned int wl) +{ + if (mode == RING_BUFFER_OVERWRITE) + return id & SB_ID_INDEX_MASK(wl); + else + return id; +} + +static +int copy_crash_subbuf(const struct lttng_crash_layout *layout, + int fd_dest, char *buf, uint64_t offset) +{ + uint64_t buf_size, subbuf_size, num_subbuf, sbidx, id, + sb_bindex, rpages_offset, p_offset, seq_cc, + committed, commit_count_mask, consumed_cur, + packet_size; + char *subbuf_ptr; + ssize_t writelen; + + /* + * Get the current subbuffer by applying the proper mask to + * "offset", and looking up the subbuf location within the + * source file buf. + */ + + buf_size = layout->buf_size; + subbuf_size = layout->subbuf_size; + num_subbuf = layout->num_subbuf; + + switch (layout->word_size) { + case 4: commit_count_mask = 0xFFFFFFFFULL / num_subbuf; + break; + case 8: commit_count_mask = 0xFFFFFFFFFFFFFFFFULL / num_subbuf; + break; + default: + ERR("Unsupported word size: %u", + (unsigned int) layout->word_size); + return -EINVAL; + } + + DBG("Copy crash subbuffer at offset %lu", offset); + sbidx = subbuf_index(offset, buf_size, subbuf_size); + + /* + * Find where the seq cc is located. Compute length of data to + * copy. + */ + seq_cc = crash_get_array_field(layout, buf, commit_hot_array, + sbidx, commit_hot_seq); + consumed_cur = crash_get_field(layout, buf, consumed_offset); + + /* + * Check that the buffer we are getting is after or at + * consumed_cur position. + */ + if ((long) subbuf_trunc(offset, subbuf_size) + - (long) subbuf_trunc(consumed_cur, subbuf_size) < 0) { + DBG("No data: position is before consumed_cur"); + goto nodata; + } + + /* + * Check if subbuffer has been fully committed. + */ + if (((seq_cc - subbuf_size) & commit_count_mask) + - (buf_trunc(offset, buf_size) / num_subbuf) + == 0) { + committed = subbuf_size; + } else { + committed = subbuf_offset(seq_cc, subbuf_size); + if (!committed) { + DBG("No data committed, seq_cc: %" PRIu64, seq_cc); + goto nodata; + } + } + + /* Find actual physical offset in subbuffer table */ + id = crash_get_array_field(layout, buf, buf_wsb_array, + sbidx, buf_wsb_id); + sb_bindex = subbuffer_id_get_index(layout->mode, id, + layout->word_size); + rpages_offset = crash_get_array_field(layout, buf, sb_array, + sb_bindex, sb_array_shmp_offset); + p_offset = crash_get_field(layout, buf + rpages_offset, + sb_backend_p_offset); + subbuf_ptr = buf + p_offset; + + if (committed == subbuf_size) { + /* + * Packet header can be used. + */ + if (layout->length.packet_size) { + memcpy(&packet_size, + subbuf_ptr + layout->offset.packet_size, + layout->length.packet_size); + if (layout->reverse_byte_order) { + packet_size = __bswap_64(packet_size); + } + packet_size /= CHAR_BIT; + } else { + packet_size = subbuf_size; + } + } else { + uint64_t patch_size; + + /* + * Find where to patch the sub-buffer header with actual + * readable data len and packet len, derived from seq + * cc. Patch it in our in-memory copy. + */ + patch_size = committed * CHAR_BIT; + if (layout->reverse_byte_order) { + patch_size = __bswap_64(patch_size); + } + if (layout->length.content_size) { + memcpy(subbuf_ptr + layout->offset.content_size, + &patch_size, layout->length.content_size); + } + if (layout->length.packet_size) { + memcpy(subbuf_ptr + layout->offset.packet_size, + &patch_size, layout->length.packet_size); + } + packet_size = committed; + } + + /* + * Copy packet into fd_dest. + */ + writelen = lttng_write(fd_dest, subbuf_ptr, packet_size); + if (writelen < packet_size) { + PERROR("Error writing to output file"); + return -1; + } + DBG("Copied %" PRIu64 " bytes of data", packet_size); + return 0; + +nodata: + return -ENODATA; +} + +static +int copy_crash_data(const struct lttng_crash_layout *layout, int fd_dest, + int fd_src) +{ + char *buf; + int ret = 0, has_data = 0; + struct stat statbuf; + size_t src_file_len; + uint64_t prod_offset, consumed_offset; + uint64_t offset, subbuf_size; + ssize_t readlen; + + ret = fstat(fd_src, &statbuf); + if (ret) { + return ret; + } + src_file_len = layout->mmap_length; + buf = zmalloc(src_file_len); + if (!buf) { + return -1; + } + readlen = lttng_read(fd_src, buf, src_file_len); + if (readlen < 0) { + PERROR("Error reading input file"); + return -1; + } + + prod_offset = crash_get_field(layout, buf, prod_offset); + DBG("prod_offset: 0x%" PRIx64, prod_offset); + consumed_offset = crash_get_field(layout, buf, consumed_offset); + DBG("consumed_offset: 0x%" PRIx64, consumed_offset); + subbuf_size = layout->subbuf_size; + + for (offset = consumed_offset; offset < prod_offset; + offset += subbuf_size) { + ret = copy_crash_subbuf(layout, fd_dest, buf, offset); + if (!ret) { + has_data = 1; + } + if (ret) { + goto end; + } + } +end: + free(buf); + if (ret && ret != -ENODATA) { + return ret; + } + if (has_data) { + return 0; + } else { + return -ENODATA; + } +} + +static +int extract_file(int output_dir_fd, const char *output_file, + int input_dir_fd, const char *input_file) +{ + int fd_dest, fd_src, ret = 0, closeret; + struct lttng_crash_layout layout; + + layout.reverse_byte_order = 0; /* For reading magic number */ + + DBG("Extract file '%s'", input_file); + fd_src = openat(input_dir_fd, input_file, O_RDONLY); + if (fd_src < 0) { + PERROR("Error opening '%s' for reading", + input_file); + ret = -1; + goto end; + } + + /* Query the crash ABI layout */ + ret = get_crash_layout(&layout, fd_src); + if (ret) { + goto close_src; + } + + fd_dest = openat(output_dir_fd, output_file, + O_RDWR | O_CREAT | O_EXCL, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + if (fd_dest < 0) { + PERROR("Error opening '%s' for writing", + output_file); + ret = -1; + goto close_src; + } + + ret = copy_crash_data(&layout, fd_dest, fd_src); + if (ret) { + goto close_dest; + } + +close_dest: + closeret = close(fd_dest); + if (closeret) { + PERROR("close"); + } + if (ret == -ENODATA) { + closeret = unlinkat(output_dir_fd, output_file, 0); + if (closeret) { + PERROR("unlinkat"); + } + } +close_src: + closeret = close(fd_src); + if (closeret) { + PERROR("close"); + } +end: + return ret; +} + +static +int extract_all_files(const char *output_path, + const char *input_path) +{ + DIR *input_dir, *output_dir; + int input_dir_fd, output_dir_fd, ret = 0, closeret; + struct dirent *entry; /* input */ + + /* Open input directory */ + input_dir = opendir(input_path); + if (!input_dir) { + PERROR("Cannot open '%s' path", input_path); + return -1; + } + input_dir_fd = dirfd(input_dir); + if (input_dir_fd < 0) { + PERROR("dirfd"); + return -1; + } + + /* Open output directory */ + output_dir = opendir(output_path); + if (!output_dir) { + PERROR("Cannot open '%s' path", output_path); + return -1; + } + output_dir_fd = dirfd(output_dir); + if (output_dir_fd < 0) { + PERROR("dirfd"); + return -1; + } + + while ((entry = readdir(input_dir))) { + if (!strcmp(entry->d_name, ".") + || !strcmp(entry->d_name, "..")) + continue; + ret = extract_file(output_dir_fd, entry->d_name, + input_dir_fd, entry->d_name); + if (ret == -ENODATA) { + DBG("No data in file '%s', skipping", entry->d_name); + ret = 0; + continue; + } else if (ret < 0) { + break; + } else if (ret > 0) { + DBG("Skipping file '%s'", entry->d_name); + ret = 0; + continue; + } + } + closeret = closedir(output_dir); + if (closeret) { + PERROR("closedir"); + } + closeret = closedir(input_dir); + if (closeret) { + PERROR("closedir"); + } + return ret; +} + +static +int extract_one_trace(const char *output_path, + const char *input_path) +{ + char dest[PATH_MAX], src[PATH_MAX]; + int ret; + + DBG("Extract crash trace '%s' into '%s'", input_path, output_path); + + /* Copy metadata */ + strncpy(dest, output_path, PATH_MAX); + dest[PATH_MAX - 1] = '\0'; + strncat(dest, "/metadata", PATH_MAX - strlen(dest) - 1); + + strncpy(src, input_path, PATH_MAX); + src[PATH_MAX - 1] = '\0'; + strncat(src, "/metadata", PATH_MAX - strlen(dest) - 1); + + ret = copy_file(dest, src); + if (ret) { + return ret; + } + + /* Extract each other file that has expected header */ + return extract_all_files(output_path, input_path); +} + +static +int extract_all_traces(const char *output_path, + char **input_paths, + int nr_input_paths) +{ + int ret, i; + int has_warning = 0; + + /* Only one input path is currently supported */ + if (nr_input_paths != 1) { + ERR("Only a single input path is currently supported.\n"); + return -1; + } + + for (i = 0; i < nr_input_paths; i++) { + ret = extract_one_trace(output_path, + input_paths[i]); + if (ret) { + WARN("Error extracting trace '%s', continuing anyway.", + input_paths[i]); + has_warning = 1; + } + } + return has_warning; +} + +static +int delete_trace(const char *trace_path) +{ + DIR *trace_dir; + int trace_dir_fd, ret = 0, closeret; + struct dirent *entry; + + /* Open trace directory */ + trace_dir = opendir(trace_path); + if (!trace_dir) { + PERROR("Cannot open '%s' path", trace_path); + return -1; + } + trace_dir_fd = dirfd(trace_dir); + if (trace_dir_fd < 0) { + PERROR("dirfd"); + return -1; + } + + while ((entry = readdir(trace_dir))) { + if (!strcmp(entry->d_name, ".") + || !strcmp(entry->d_name, "..")) + continue; + switch (entry->d_type) { + case DT_DIR: + unlinkat(trace_dir_fd, entry->d_name, AT_REMOVEDIR); + break; + case DT_REG: + unlinkat(trace_dir_fd, entry->d_name, 0); + break; + default: + ret = -EINVAL; + goto end; + } + } +end: + closeret = closedir(trace_dir); + if (closeret) { + PERROR("closedir"); + } + if (!ret) { + ret = rmdir(trace_path); + } + return ret; +} + + +static +int view_trace(const char *viewer_path, const char *trace_path) +{ + pid_t pid; + + pid = fork(); + if (pid < 0) { + /* Error */ + PERROR("fork"); + return -1; + } else if (pid > 0) { + /* Parent */ + int status; + + pid = waitpid(pid, &status, 0); + if (pid < 0 || !WIFEXITED(status)) { + return -1; + } + } else { + /* Child */ + int ret; + + ret = execlp(viewer_path, viewer_path, + trace_path, (char *) NULL); + if (ret) { + PERROR("execlp"); + exit(EXIT_FAILURE); + } + exit(EXIT_SUCCESS); /* Never reached */ + } + return 0; +} + +/* + * main + */ +int main(int argc, char *argv[]) +{ + int ret, has_warning = 0; + const char *output_path = NULL; + char tmppath[] = "/tmp/lttng-crash-XXXXXX"; + + progname = argv[0] ? argv[0] : "lttng-crash"; + + ret = parse_args(argc, argv); + if (ret > 0) { + exit(EXIT_SUCCESS); + } else if (ret < 0) { + exit(EXIT_FAILURE); + } + + if (opt_output_path) { + output_path = opt_output_path; + ret = mkdir(output_path, S_IRWXU | S_IRWXG); + if (ret) { + PERROR("mkdir"); + exit(EXIT_FAILURE); + } + } else { + output_path = mkdtemp(tmppath); + if (!output_path) { + PERROR("mkdtemp"); + exit(EXIT_FAILURE); + } + } + + ret = extract_all_traces(output_path, input_paths, + nr_input_paths); + if (ret < 0) { + exit(EXIT_FAILURE); + } else if (ret > 0) { + has_warning = 1; + } + if (!opt_output_path) { + /* View trace */ + ret = view_trace(opt_viewer_path, output_path); + if (ret) + has_warning = 1; + + /* unlink temporary trace */ + ret = delete_trace(output_path); + if (ret) + has_warning = 1; + } + if (has_warning) + exit(EXIT_FAILURE); + exit(EXIT_SUCCESS); +} diff --git a/src/bin/lttng-sessiond/buffer-registry.c b/src/bin/lttng-sessiond/buffer-registry.c index 0ff76defb..b96e58500 100644 --- a/src/bin/lttng-sessiond/buffer-registry.c +++ b/src/bin/lttng-sessiond/buffer-registry.c @@ -106,7 +106,8 @@ void buffer_reg_init_uid_registry(void) * Return 0 on success else a negative value and regp is untouched. */ int buffer_reg_uid_create(uint64_t session_id, uint32_t bits_per_long, uid_t uid, - enum lttng_domain_type domain, struct buffer_reg_uid **regp) + enum lttng_domain_type domain, struct buffer_reg_uid **regp, + const char *shm_path) { int ret = 0; struct buffer_reg_uid *reg = NULL; @@ -131,7 +132,12 @@ int buffer_reg_uid_create(uint64_t session_id, uint32_t bits_per_long, uid_t uid reg->bits_per_long = bits_per_long; reg->uid = uid; reg->domain = domain; - + if (shm_path[0]) { + strncpy(reg->shm_path, shm_path, sizeof(reg->shm_path)); + reg->shm_path[sizeof(reg->shm_path) - 1] = '\0'; + DBG3("shm path '%s' is assigned to uid buffer registry for session id %" PRIu64, + reg->shm_path, session_id); + } reg->registry->channels = lttng_ht_new(0, LTTNG_HT_TYPE_U64); if (!reg->registry->channels) { ret = -ENOMEM; @@ -226,7 +232,8 @@ void buffer_reg_init_pid_registry(void) * * Return 0 on success else a negative value and regp is untouched. */ -int buffer_reg_pid_create(uint64_t session_id, struct buffer_reg_pid **regp) +int buffer_reg_pid_create(uint64_t session_id, struct buffer_reg_pid **regp, + const char *shm_path) { int ret = 0; struct buffer_reg_pid *reg = NULL; @@ -249,7 +256,12 @@ int buffer_reg_pid_create(uint64_t session_id, struct buffer_reg_pid **regp) /* A cast is done here so we can use the session ID as a u64 ht node. */ reg->session_id = session_id; - + if (shm_path[0]) { + strncpy(reg->shm_path, shm_path, sizeof(reg->shm_path)); + reg->shm_path[sizeof(reg->shm_path) - 1] = '\0'; + DBG3("shm path '%s' is assigned to pid buffer registry for session id %" PRIu64, + reg->shm_path, session_id); + } reg->registry->channels = lttng_ht_new(0, LTTNG_HT_TYPE_U64); if (!reg->registry->channels) { ret = -ENOMEM; diff --git a/src/bin/lttng-sessiond/buffer-registry.h b/src/bin/lttng-sessiond/buffer-registry.h index 656fc9175..d17bbab62 100644 --- a/src/bin/lttng-sessiond/buffer-registry.h +++ b/src/bin/lttng-sessiond/buffer-registry.h @@ -88,6 +88,8 @@ struct buffer_reg_uid { struct lttng_ht_node_u64 node; /* Node of a linked list used to teardown object at a destroy session. */ struct cds_list_head lnode; + + char shm_path[PATH_MAX]; }; /* @@ -100,12 +102,15 @@ struct buffer_reg_pid { /* Indexed by session id. */ struct lttng_ht_node_u64 node; + + char shm_path[PATH_MAX]; }; /* Buffer registry per UID. */ void buffer_reg_init_uid_registry(void); int buffer_reg_uid_create(uint64_t session_id, uint32_t bits_per_long, uid_t uid, - enum lttng_domain_type domain, struct buffer_reg_uid **regp); + enum lttng_domain_type domain, struct buffer_reg_uid **regp, + const char *shm_path); void buffer_reg_uid_add(struct buffer_reg_uid *reg); struct buffer_reg_uid *buffer_reg_uid_find(uint64_t session_id, uint32_t bits_per_long, uid_t uid); @@ -115,7 +120,8 @@ void buffer_reg_uid_destroy(struct buffer_reg_uid *regp, /* Buffer registry per PID. */ void buffer_reg_init_pid_registry(void); -int buffer_reg_pid_create(uint64_t session_id, struct buffer_reg_pid **regp); +int buffer_reg_pid_create(uint64_t session_id, struct buffer_reg_pid **regp, + const char *shm_path); void buffer_reg_pid_add(struct buffer_reg_pid *reg); struct buffer_reg_pid *buffer_reg_pid_find(uint64_t session_id); void buffer_reg_pid_remove(struct buffer_reg_pid *regp); diff --git a/src/bin/lttng-sessiond/cmd.c b/src/bin/lttng-sessiond/cmd.c index cdc58b8cf..bf6097b64 100644 --- a/src/bin/lttng-sessiond/cmd.c +++ b/src/bin/lttng-sessiond/cmd.c @@ -3333,6 +3333,29 @@ error: return ret; } +/* + * Command LTTNG_SET_SESSION_SHM_PATH processed by the client thread. + */ +int cmd_set_session_shm_path(struct ltt_session *session, + const char *shm_path) +{ + /* Safety net */ + assert(session); + + /* + * Can only set shm path before session is started. + */ + if (session->has_been_started) { + return LTTNG_ERR_SESSION_STARTED; + } + + strncpy(session->shm_path, shm_path, + sizeof(session->shm_path)); + session->shm_path[sizeof(session->shm_path) - 1] = '\0'; + + return 0; +} + /* * Init command subsystem. */ diff --git a/src/bin/lttng-sessiond/cmd.h b/src/bin/lttng-sessiond/cmd.h index 69c931a38..7144690ca 100644 --- a/src/bin/lttng-sessiond/cmd.h +++ b/src/bin/lttng-sessiond/cmd.h @@ -97,4 +97,7 @@ int cmd_snapshot_del_output(struct ltt_session *session, int cmd_snapshot_record(struct ltt_session *session, struct lttng_snapshot_output *output, int wait); +int cmd_set_session_shm_path(struct ltt_session *session, + const char *shm_path); + #endif /* CMD_H */ diff --git a/src/bin/lttng-sessiond/consumer.c b/src/bin/lttng-sessiond/consumer.c index b7394d97d..628201e07 100644 --- a/src/bin/lttng-sessiond/consumer.c +++ b/src/bin/lttng-sessiond/consumer.c @@ -801,7 +801,8 @@ void consumer_init_ask_channel_comm_msg(struct lttcomm_consumer_msg *msg, uint64_t tracefile_count, uint64_t session_id_per_pid, unsigned int monitor, - uint32_t ust_app_uid) + uint32_t ust_app_uid, + const char *shm_path) { assert(msg); @@ -839,6 +840,12 @@ void consumer_init_ask_channel_comm_msg(struct lttcomm_consumer_msg *msg, strncpy(msg->u.ask_channel.name, name, sizeof(msg->u.ask_channel.name)); msg->u.ask_channel.name[sizeof(msg->u.ask_channel.name) - 1] = '\0'; + + if (shm_path) { + strncpy(msg->u.ask_channel.shm_path, shm_path, + sizeof(msg->u.ask_channel.shm_path)); + msg->u.ask_channel.shm_path[sizeof(msg->u.ask_channel.shm_path) - 1] = '\0'; + } } /* diff --git a/src/bin/lttng-sessiond/consumer.h b/src/bin/lttng-sessiond/consumer.h index a9266d5b9..0b67c0828 100644 --- a/src/bin/lttng-sessiond/consumer.h +++ b/src/bin/lttng-sessiond/consumer.h @@ -240,7 +240,8 @@ void consumer_init_ask_channel_comm_msg(struct lttcomm_consumer_msg *msg, uint64_t tracefile_count, uint64_t session_id_per_pid, unsigned int monitor, - uint32_t ust_app_uid); + uint32_t ust_app_uid, + const char *shm_path); void consumer_init_stream_comm_msg(struct lttcomm_consumer_msg *msg, enum lttng_consumer_command cmd, uint64_t channel_key, diff --git a/src/bin/lttng-sessiond/main.c b/src/bin/lttng-sessiond/main.c index e68aa79d4..c54217937 100644 --- a/src/bin/lttng-sessiond/main.c +++ b/src/bin/lttng-sessiond/main.c @@ -2765,7 +2765,13 @@ static int create_ust_session(struct ltt_session *session, lus->snapshot_mode = session->snapshot_mode; lus->live_timer_interval = session->live_timer; session->ust_session = lus; - + if (session->shm_path[0]) { + strncpy(lus->shm_path, session->shm_path, + sizeof(lus->shm_path)); + lus->shm_path[sizeof(lus->shm_path) - 1] = '\0'; + strncat(lus->shm_path, "/ust", + sizeof(lus->shm_path) - strlen(lus->shm_path) - 1); + } /* Copy session output to the newly created UST session */ ret = copy_session_consumer(domain->type, session); if (ret != LTTNG_OK) { @@ -2890,6 +2896,7 @@ static int process_client_msg(struct command_ctx *cmd_ctx, int sock, case LTTNG_SNAPSHOT_LIST_OUTPUT: case LTTNG_SNAPSHOT_RECORD: case LTTNG_SAVE_SESSION: + case LTTNG_SET_SESSION_SHM_PATH: need_domain = 0; break; default: @@ -3830,6 +3837,12 @@ skip_domain: &cmd_ctx->creds); break; } + case LTTNG_SET_SESSION_SHM_PATH: + { + ret = cmd_set_session_shm_path(cmd_ctx->session, + cmd_ctx->lsm->u.set_shm_path.shm_path); + break; + } default: ret = LTTNG_ERR_UND; break; diff --git a/src/bin/lttng-sessiond/session.h b/src/bin/lttng-sessiond/session.h index 368b352fa..48dc44a33 100644 --- a/src/bin/lttng-sessiond/session.h +++ b/src/bin/lttng-sessiond/session.h @@ -109,6 +109,10 @@ struct ltt_session { * Timer set when the session is created for live reading. */ unsigned int live_timer; + /* + * Path where to keep the shared memory files. + */ + char shm_path[PATH_MAX]; }; /* Prototypes */ diff --git a/src/bin/lttng-sessiond/trace-ust.h b/src/bin/lttng-sessiond/trace-ust.h index 795389e9d..63564ff71 100644 --- a/src/bin/lttng-sessiond/trace-ust.h +++ b/src/bin/lttng-sessiond/trace-ust.h @@ -113,6 +113,11 @@ struct ltt_ust_session { /* Metadata channel attributes. */ struct lttng_ust_channel_attr metadata_attr; + + /* + * Path where to keep the shared memory files. + */ + char shm_path[PATH_MAX]; }; /* diff --git a/src/bin/lttng-sessiond/ust-app.c b/src/bin/lttng-sessiond/ust-app.c index c8cff2386..480971b89 100644 --- a/src/bin/lttng-sessiond/ust-app.c +++ b/src/bin/lttng-sessiond/ust-app.c @@ -1593,6 +1593,7 @@ static void shadow_copy_session(struct ust_app_session *ua_sess, struct tm *timeinfo; char datetime[16]; int ret; + char tmp_shm_path[PATH_MAX]; /* Get date and time for unique app path */ time(&rawtime); @@ -1636,6 +1637,35 @@ static void shadow_copy_session(struct ust_app_session *ua_sess, goto error; } + strncpy(ua_sess->shm_path, usess->shm_path, + sizeof(ua_sess->shm_path)); + ua_sess->shm_path[sizeof(ua_sess->shm_path) - 1] = '\0'; + if (ua_sess->shm_path[0]) { + switch (ua_sess->buffer_type) { + case LTTNG_BUFFER_PER_PID: + ret = snprintf(tmp_shm_path, sizeof(tmp_shm_path), + DEFAULT_UST_TRACE_PID_PATH "/%s-%d-%s", + app->name, app->pid, datetime); + break; + case LTTNG_BUFFER_PER_UID: + ret = snprintf(tmp_shm_path, sizeof(tmp_shm_path), + DEFAULT_UST_TRACE_UID_PATH, + app->uid, app->bits_per_long); + break; + default: + assert(0); + goto error; + } + if (ret < 0) { + PERROR("sprintf UST shadow copy session"); + assert(0); + goto error; + } + strncat(ua_sess->shm_path, tmp_shm_path, + sizeof(ua_sess->shm_path) - strlen(ua_sess->shm_path) - 1); + ua_sess->shm_path[sizeof(ua_sess->shm_path) - 1] = '\0'; + } + /* Iterate over all channels in global domain. */ cds_lfht_for_each_entry(usess->domain_global.channels->ht, &iter.iter, uchan, node.node) { @@ -1727,7 +1757,8 @@ static int setup_buffer_reg_pid(struct ust_app_session *ua_sess, * This is the create channel path meaning that if there is NO * registry available, we have to create one for this session. */ - ret = buffer_reg_pid_create(ua_sess->id, ®_pid); + ret = buffer_reg_pid_create(ua_sess->id, ®_pid, + ua_sess->shm_path); if (ret < 0) { goto error; } @@ -1741,7 +1772,8 @@ static int setup_buffer_reg_pid(struct ust_app_session *ua_sess, app->uint16_t_alignment, app->uint32_t_alignment, app->uint64_t_alignment, app->long_alignment, app->byte_order, app->version.major, - app->version.minor); + app->version.minor, reg_pid->shm_path, + ua_sess->euid, ua_sess->egid); if (ret < 0) { /* * reg_pid->registry->reg.ust is NULL upon error, so we need to @@ -1774,6 +1806,7 @@ error: * Return 0 on success or else a negative value. */ static int setup_buffer_reg_uid(struct ltt_ust_session *usess, + struct ust_app_session *ua_sess, struct ust_app *app, struct buffer_reg_uid **regp) { int ret = 0; @@ -1791,7 +1824,7 @@ static int setup_buffer_reg_uid(struct ltt_ust_session *usess, * registry available, we have to create one for this session. */ ret = buffer_reg_uid_create(usess->id, app->bits_per_long, app->uid, - LTTNG_DOMAIN_UST, ®_uid); + LTTNG_DOMAIN_UST, ®_uid, ua_sess->shm_path); if (ret < 0) { goto error; } @@ -1805,7 +1838,8 @@ static int setup_buffer_reg_uid(struct ltt_ust_session *usess, app->uint16_t_alignment, app->uint32_t_alignment, app->uint64_t_alignment, app->long_alignment, app->byte_order, app->version.major, - app->version.minor); + app->version.minor, reg_uid->shm_path, + usess->uid, usess->gid); if (ret < 0) { /* * reg_uid->registry->reg.ust is NULL upon error, so we need to @@ -1880,7 +1914,7 @@ static int create_ust_app_session(struct ltt_ust_session *usess, break; case LTTNG_BUFFER_PER_UID: /* Look for a global registry. If none exists, create one. */ - ret = setup_buffer_reg_uid(usess, app, NULL); + ret = setup_buffer_reg_uid(usess, ua_sess, app, NULL); if (ret < 0) { delete_ust_app_session(-1, ua_sess, app); goto error; diff --git a/src/bin/lttng-sessiond/ust-app.h b/src/bin/lttng-sessiond/ust-app.h index 9347cd709..59ef85b78 100644 --- a/src/bin/lttng-sessiond/ust-app.h +++ b/src/bin/lttng-sessiond/ust-app.h @@ -215,6 +215,8 @@ struct ust_app_session { /* Metadata channel attributes. */ struct ustctl_consumer_channel_attr metadata_attr; + + char shm_path[PATH_MAX]; }; /* diff --git a/src/bin/lttng-sessiond/ust-consumer.c b/src/bin/lttng-sessiond/ust-consumer.c index 611b54dcd..8a3533ba1 100644 --- a/src/bin/lttng-sessiond/ust-consumer.c +++ b/src/bin/lttng-sessiond/ust-consumer.c @@ -109,6 +109,7 @@ static int ask_channel_creation(struct ust_app_session *ua_sess, char *pathname = NULL; struct lttcomm_consumer_msg msg; struct ust_registry_channel *chan_reg; + char shm_path[PATH_MAX] = ""; assert(ua_sess); assert(ua_chan); @@ -136,10 +137,25 @@ static int ask_channel_creation(struct ust_app_session *ua_sess, if (ua_chan->attr.type == LTTNG_UST_CHAN_METADATA) { chan_id = -1U; + /* + * Metadata channels shm_path (buffers) are handled within + * session daemon. Consumer daemon should not try to create + * those buffer files. + */ } else { chan_reg = ust_registry_channel_find(registry, chan_reg_key); assert(chan_reg); chan_id = chan_reg->chan_id; + if (ua_sess->shm_path[0]) { + strncpy(shm_path, ua_sess->shm_path, sizeof(shm_path)); + shm_path[sizeof(shm_path) - 1] = '\0'; + strncat(shm_path, "/", + sizeof(shm_path) - strlen(shm_path) - 1); + strncat(shm_path, ua_chan->name, + sizeof(shm_path) - strlen(shm_path) - 1); + strncat(shm_path, "_", + sizeof(shm_path) - strlen(shm_path) - 1); + } } switch (ua_chan->attr.output) { @@ -171,7 +187,8 @@ static int ask_channel_creation(struct ust_app_session *ua_sess, ua_chan->tracefile_count, ua_sess->id, ua_sess->output_traces, - ua_sess->uid); + ua_sess->uid, + shm_path); health_code_update(); diff --git a/src/bin/lttng-sessiond/ust-metadata.c b/src/bin/lttng-sessiond/ust-metadata.c index 379429285..f4f273b16 100644 --- a/src/bin/lttng-sessiond/ust-metadata.c +++ b/src/bin/lttng-sessiond/ust-metadata.c @@ -120,6 +120,23 @@ ssize_t metadata_reserve(struct ust_registry_session *session, size_t len) return ret; } +static +int metadata_file_append(struct ust_registry_session *session, + const char *str, size_t len) +{ + ssize_t written; + + if (session->metadata_fd < 0) { + return 0; + } + /* Write to metadata file */ + written = lttng_write(session->metadata_fd, str, len); + if (written != len) { + return -1; + } + return 0; +} + /* * We have exclusive access to our metadata buffer (protected by the * ust_lock), so we can do racy operations such as looking for @@ -149,6 +166,11 @@ int lttng_metadata_printf(struct ust_registry_session *session, goto end; } memcpy(&session->metadata[offset], str, len); + ret = metadata_file_append(session, str, len); + if (ret) { + PERROR("Error appending to metadata file"); + goto end; + } DBG3("Append to metadata: \"%s\"", str); ret = 0; @@ -575,6 +597,15 @@ int ust_metadata_session_statedump(struct ust_registry_session *session, uuid_c[8], uuid_c[9], uuid_c[10], uuid_c[11], uuid_c[12], uuid_c[13], uuid_c[14], uuid_c[15]); + /* For crash ABI */ + ret = lttng_metadata_printf(session, + "/* CTF %u.%u */\n\n", + CTF_SPEC_MAJOR, + CTF_SPEC_MINOR); + if (ret) { + goto end; + } + ret = lttng_metadata_printf(session, "typealias integer { size = 8; align = %u; signed = false; } := uint8_t;\n" "typealias integer { size = 16; align = %u; signed = false; } := uint16_t;\n" diff --git a/src/bin/lttng-sessiond/ust-registry.c b/src/bin/lttng-sessiond/ust-registry.c index 1a00e9349..26f306d52 100644 --- a/src/bin/lttng-sessiond/ust-registry.c +++ b/src/bin/lttng-sessiond/ust-registry.c @@ -546,7 +546,10 @@ int ust_registry_session_init(struct ust_registry_session **sessionp, uint32_t long_alignment, int byte_order, uint32_t major, - uint32_t minor) + uint32_t minor, + const char *shm_path, + uid_t euid, + gid_t egid) { int ret; struct ust_registry_session *session; @@ -567,6 +570,38 @@ int ust_registry_session_init(struct ust_registry_session **sessionp, session->uint64_t_alignment = uint64_t_alignment; session->long_alignment = long_alignment; session->byte_order = byte_order; + session->metadata_fd = -1; + if (shm_path[0]) { + strncpy(session->shm_path, shm_path, + sizeof(session->shm_path)); + session->shm_path[sizeof(session->shm_path) - 1] = '\0'; + strncpy(session->metadata_path, shm_path, + sizeof(session->metadata_path)); + session->metadata_path[sizeof(session->metadata_path) - 1] = '\0'; + strncat(session->metadata_path, "/metadata", + sizeof(session->metadata_path) + - strlen(session->metadata_path) - 1); + } + if (session->shm_path[0]) { + ret = run_as_mkdir_recursive(session->shm_path, + S_IRWXU | S_IRWXG, + euid, egid); + if (ret) { + PERROR("run_as_mkdir_recursive"); + goto error; + } + } + if (session->metadata_path[0]) { + /* Create metadata file */ + ret = open(session->metadata_path, + O_WRONLY | O_CREAT | O_EXCL, + S_IRUSR | S_IWUSR); + if (ret < 0) { + PERROR("Opening metadata file"); + goto error; + } + session->metadata_fd = ret; + } session->channels = lttng_ht_new(0, LTTNG_HT_TYPE_U64); if (!session->channels) { @@ -631,4 +666,14 @@ void ust_registry_session_destroy(struct ust_registry_session *reg) } free(reg->metadata); + if (reg->metadata_fd >= 0) { + ret = close(reg->metadata_fd); + if (ret) { + PERROR("close"); + } + ret = unlink(reg->metadata_path); + if (ret) { + PERROR("unlink"); + } + } } diff --git a/src/bin/lttng-sessiond/ust-registry.h b/src/bin/lttng-sessiond/ust-registry.h index 0cba8a334..a22009efa 100644 --- a/src/bin/lttng-sessiond/ust-registry.h +++ b/src/bin/lttng-sessiond/ust-registry.h @@ -66,6 +66,11 @@ struct ust_registry_session { size_t metadata_len, metadata_alloc_len; /* Length of bytes sent to the consumer. */ size_t metadata_len_sent; + + char shm_path[PATH_MAX]; + char metadata_path[PATH_MAX]; + int metadata_fd; /* file-backed metadata FD */ + /* * Hash table containing channels sent by the UST tracer. MUST * be accessed with a RCU read side lock acquired. @@ -225,7 +230,10 @@ int ust_registry_session_init(struct ust_registry_session **sessionp, uint32_t long_alignment, int byte_order, uint32_t major, - uint32_t minor); + uint32_t minor, + const char *shm_path, + uid_t euid, + gid_t egid); void ust_registry_session_destroy(struct ust_registry_session *session); int ust_registry_create_event(struct ust_registry_session *session, diff --git a/src/bin/lttng/commands/create.c b/src/bin/lttng/commands/create.c index 1ebe590ad..b8d9f3b56 100644 --- a/src/bin/lttng/commands/create.c +++ b/src/bin/lttng/commands/create.c @@ -44,6 +44,7 @@ static char *opt_session_name; static char *opt_url; static char *opt_ctrl_url; static char *opt_data_url; +static char *opt_shm_path; static int opt_no_consumer; static int opt_no_output; static int opt_snapshot; @@ -69,6 +70,7 @@ static struct poptOption long_options[] = { {"no-consumer", 0, POPT_ARG_VAL, &opt_no_consumer, 1, 0, 0}, {"snapshot", 0, POPT_ARG_VAL, &opt_snapshot, 1, 0, 0}, {"live", 0, POPT_ARG_INT | POPT_ARGFLAG_OPTIONAL, 0, OPT_LIVE_TIMER, 0, 0}, + {"shm-path", 0, POPT_ARG_STRING, &opt_shm_path, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0} }; @@ -106,6 +108,9 @@ static void usage(FILE *ofp) fprintf(ofp, " By default, %u is used for the timer and the\n", DEFAULT_LTTNG_LIVE_TIMER); fprintf(ofp, " network URL is set to net://127.0.0.1.\n"); + fprintf(ofp, " --shm-path PATH Path where shared memory holding buffers\n"); + fprintf(ofp, " should be created. Useful when used with pramfs\n"); + fprintf(ofp, " to extract trace data after crash.\n"); fprintf(ofp, "\n"); fprintf(ofp, "Extended Options:\n"); fprintf(ofp, "\n"); @@ -297,6 +302,7 @@ static int create_session(void) char session_name_date[NAME_MAX + 17], *print_str_url = NULL; time_t rawtime; struct tm *timeinfo; + char shm_path[PATH_MAX] = ""; /* Get date and time for automatic session name/path */ time(&rawtime); @@ -481,6 +487,21 @@ static int create_session(void) } } + if (opt_shm_path) { + ret = snprintf(shm_path, sizeof(shm_path), + "%s/%s", opt_shm_path, session_name_date); + if (ret < 0) { + PERROR("snprintf shm_path"); + goto error; + } + + ret = lttng_set_session_shm_path(session_name, shm_path); + if (ret < 0) { + lttng_destroy_session(session_name); + goto error; + } + } + MSG("Session %s created.", session_name); if (print_str_url && !opt_snapshot) { MSG("Traces will be written in %s", print_str_url); @@ -495,6 +516,10 @@ static int create_session(void) MSG("Snapshot mode set. Every channel enabled for that session will " "be set in overwrite mode and mmap output."); } + if (opt_shm_path) { + MSG("Session %s set to shm_path: %s.", session_name, + shm_path); + } /* Mi output */ if (lttng_opt_mi) { diff --git a/src/common/consumer.c b/src/common/consumer.c index 0af994fc4..1cb1c4743 100644 --- a/src/common/consumer.c +++ b/src/common/consumer.c @@ -936,7 +936,8 @@ struct lttng_consumer_channel *consumer_allocate_channel(uint64_t key, uint64_t tracefile_count, uint64_t session_id_per_pid, unsigned int monitor, - unsigned int live_timer_interval) + unsigned int live_timer_interval, + const char *shm_path) { struct lttng_consumer_channel *channel; @@ -993,6 +994,11 @@ struct lttng_consumer_channel *consumer_allocate_channel(uint64_t key, strncpy(channel->name, name, sizeof(channel->name)); channel->name[sizeof(channel->name) - 1] = '\0'; + if (shm_path) { + strncpy(channel->shm_path, shm_path, sizeof(channel->shm_path)); + channel->shm_path[sizeof(channel->shm_path) - 1] = '\0'; + } + lttng_ht_node_init_u64(&channel->node, channel->key); channel->wait_fd = -1; diff --git a/src/common/consumer.h b/src/common/consumer.h index 790cb6b13..a80bc40ab 100644 --- a/src/common/consumer.h +++ b/src/common/consumer.h @@ -206,6 +206,8 @@ struct lttng_consumer_channel { /* Timer value in usec for live streaming. */ unsigned int live_timer_interval; + + char shm_path[PATH_MAX]; }; /* @@ -599,7 +601,8 @@ struct lttng_consumer_channel *consumer_allocate_channel(uint64_t key, uint64_t tracefile_count, uint64_t session_id_per_pid, unsigned int monitor, - unsigned int live_timer_interval); + unsigned int live_timer_interval, + const char *shm_path); void consumer_del_stream(struct lttng_consumer_stream *stream, struct lttng_ht *ht); void consumer_del_metadata_stream(struct lttng_consumer_stream *stream, diff --git a/src/common/kernel-consumer/kernel-consumer.c b/src/common/kernel-consumer/kernel-consumer.c index 7622a2253..0af417b9c 100644 --- a/src/common/kernel-consumer/kernel-consumer.c +++ b/src/common/kernel-consumer/kernel-consumer.c @@ -480,7 +480,8 @@ int lttng_kconsumer_recv_cmd(struct lttng_consumer_local_data *ctx, msg.u.channel.tracefile_size, msg.u.channel.tracefile_count, 0, msg.u.channel.monitor, - msg.u.channel.live_timer_interval); + msg.u.channel.live_timer_interval, + NULL); if (new_channel == NULL) { lttng_consumer_send_error(ctx, LTTCOMM_CONSUMERD_OUTFD_ERROR); goto end_nosignal; diff --git a/src/common/sessiond-comm/sessiond-comm.h b/src/common/sessiond-comm/sessiond-comm.h index 1426fcf20..baf608ffd 100644 --- a/src/common/sessiond-comm/sessiond-comm.h +++ b/src/common/sessiond-comm/sessiond-comm.h @@ -91,6 +91,9 @@ enum lttcomm_sessiond_command { LTTNG_CREATE_SESSION_SNAPSHOT = 29, LTTNG_CREATE_SESSION_LIVE = 30, LTTNG_SAVE_SESSION = 31, + + /* Session daemon commands (cont.) */ + LTTNG_SET_SESSION_SHM_PATH = 40, }; enum lttcomm_relayd_command { @@ -296,6 +299,9 @@ struct lttcomm_session_msg { struct { struct lttng_save_session_attr attr; /* struct already packed */ } LTTNG_PACKED save_session; + struct { + char shm_path[PATH_MAX]; + } LTTNG_PACKED set_shm_path; } u; } LTTNG_PACKED; @@ -424,6 +430,7 @@ struct lttcomm_consumer_msg { * because the application can be in the tracing for instance. */ uint32_t ust_app_uid; + char shm_path[PATH_MAX]; } LTTNG_PACKED ask_channel; struct { uint64_t key; diff --git a/src/common/ust-consumer/ust-consumer.c b/src/common/ust-consumer/ust-consumer.c index bf0208f1d..81f685912 100644 --- a/src/common/ust-consumer/ust-consumer.c +++ b/src/common/ust-consumer/ust-consumer.c @@ -124,14 +124,16 @@ static struct lttng_consumer_channel *allocate_channel(uint64_t session_id, uint64_t relayd_id, uint64_t key, enum lttng_event_output output, uint64_t tracefile_size, uint64_t tracefile_count, uint64_t session_id_per_pid, unsigned int monitor, - unsigned int live_timer_interval) + unsigned int live_timer_interval, + const char *shm_path) { assert(pathname); assert(name); return consumer_allocate_channel(key, session_id, pathname, name, uid, gid, relayd_id, output, tracefile_size, - tracefile_count, session_id_per_pid, monitor, live_timer_interval); + tracefile_count, session_id_per_pid, monitor, + live_timer_interval, shm_path); } /* @@ -1210,7 +1212,8 @@ int lttng_ustconsumer_recv_cmd(struct lttng_consumer_local_data *ctx, msg.u.ask_channel.tracefile_count, msg.u.ask_channel.session_id_per_pid, msg.u.ask_channel.monitor, - msg.u.ask_channel.live_timer_interval); + msg.u.ask_channel.live_timer_interval, + msg.u.ask_channel.shm_path); if (!channel) { goto end_channel_error; } @@ -1230,6 +1233,9 @@ int lttng_ustconsumer_recv_cmd(struct lttng_consumer_local_data *ctx, attr.read_timer_interval = msg.u.ask_channel.read_timer_interval; attr.chan_id = msg.u.ask_channel.chan_id; memcpy(attr.uuid, msg.u.ask_channel.uuid, sizeof(attr.uuid)); + strncpy(attr.shm_path, channel->shm_path, + sizeof(attr.shm_path)); + attr.shm_path[sizeof(attr.shm_path) - 1] = '\0'; /* Match channel buffer type to the UST abi. */ switch (msg.u.ask_channel.output) { diff --git a/src/lib/lttng-ctl/lttng-ctl.c b/src/lib/lttng-ctl/lttng-ctl.c index f879d8ddf..4332881c6 100644 --- a/src/lib/lttng-ctl/lttng-ctl.c +++ b/src/lib/lttng-ctl/lttng-ctl.c @@ -1452,6 +1452,26 @@ int lttng_list_sessions(struct lttng_session **sessions) return ret / sizeof(struct lttng_session); } +int lttng_set_session_shm_path(const char *session_name, + const char *shm_path) +{ + struct lttcomm_session_msg lsm; + + if (session_name == NULL) { + return -LTTNG_ERR_INVALID; + } + + memset(&lsm, 0, sizeof(lsm)); + lsm.cmd_type = LTTNG_SET_SESSION_SHM_PATH; + + lttng_ctl_copy_string(lsm.session.name, session_name, + sizeof(lsm.session.name)); + lttng_ctl_copy_string(lsm.u.set_shm_path.shm_path, shm_path, + sizeof(lsm.u.set_shm_path.shm_path)); + + return lttng_ctl_ask_sessiond(&lsm, NULL); +} + /* * Ask the session daemon for all available domains of a session. * Sets the contents of the domains array.