+/*
+ * Create a kernel event notifier group, register it to the kernel tracer and
+ * add it to the kernel session.
+ */
+static int kernel_create_event_notifier_group(int *event_notifier_group_fd)
+{
+ int ret;
+ int local_fd = -1;
+
+ assert(event_notifier_group_fd);
+
+ /* Kernel event notifier group creation. */
+ ret = kernctl_create_event_notifier_group(kernel_tracer_fd);
+ if (ret < 0) {
+ PERROR("Failed to create kernel event notifier group");
+ ret = -1;
+ goto error;
+ }
+
+ local_fd = ret;
+
+ /* Prevent fd duplication after execlp(). */
+ ret = fcntl(local_fd, F_SETFD, FD_CLOEXEC);
+ if (ret < 0) {
+ PERROR("Failed to set FD_CLOEXEC on kernel event notifier group file descriptor: fd = %d",
+ local_fd);
+ goto error;
+ }
+
+ DBG("Created kernel event notifier group: fd = %d", local_fd);
+ *event_notifier_group_fd = local_fd;
+ local_fd = -1;
+ ret = 0;
+error:
+ if (local_fd >= 0) {
+ ret = close(local_fd);
+ if (ret) {
+ PERROR("Failed to close kernel event notifier group file descriptor: fd = %d",
+ local_fd);
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * Compute the offset of the instrumentation byte in the binary based on the
+ * function probe location using the ELF lookup method.
+ *
+ * Returns 0 on success and set the offset out parameter to the offset of the
+ * elf symbol
+ * Returns -1 on error
+ */
+static
+int extract_userspace_probe_offset_function_elf(
+ const struct lttng_userspace_probe_location *probe_location,
+ uid_t uid, gid_t gid, uint64_t *offset)
+{
+ int fd;
+ int ret = 0;
+ const char *symbol = NULL;
+ const struct lttng_userspace_probe_location_lookup_method *lookup = NULL;
+ enum lttng_userspace_probe_location_lookup_method_type lookup_method_type;
+
+ assert(lttng_userspace_probe_location_get_type(probe_location) ==
+ LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION);
+
+ lookup = lttng_userspace_probe_location_get_lookup_method(
+ probe_location);
+ if (!lookup) {
+ ret = -1;
+ goto end;
+ }
+
+ lookup_method_type =
+ lttng_userspace_probe_location_lookup_method_get_type(lookup);
+
+ assert(lookup_method_type ==
+ LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF);
+
+ symbol = lttng_userspace_probe_location_function_get_function_name(
+ probe_location);
+ if (!symbol) {
+ ret = -1;
+ goto end;
+ }
+
+ fd = lttng_userspace_probe_location_function_get_binary_fd(probe_location);
+ if (fd < 0) {
+ ret = -1;
+ goto end;
+ }
+
+ ret = run_as_extract_elf_symbol_offset(fd, symbol, uid, gid, offset);
+ if (ret < 0) {
+ DBG("userspace probe offset calculation failed for "
+ "function %s", symbol);
+ goto end;
+ }
+
+ DBG("userspace probe elf offset for %s is 0x%jd", symbol, (intmax_t)(*offset));
+end:
+ return ret;
+}
+
+/*
+ * Compute the offsets of the instrumentation bytes in the binary based on the
+ * tracepoint probe location using the SDT lookup method. This function
+ * allocates the offsets buffer, the caller must free it.
+ *
+ * Returns 0 on success and set the offset out parameter to the offsets of the
+ * SDT tracepoint.
+ * Returns -1 on error.
+ */
+static
+int extract_userspace_probe_offset_tracepoint_sdt(
+ const struct lttng_userspace_probe_location *probe_location,
+ uid_t uid, gid_t gid, uint64_t **offsets,
+ uint32_t *offsets_count)
+{
+ enum lttng_userspace_probe_location_lookup_method_type lookup_method_type;
+ const struct lttng_userspace_probe_location_lookup_method *lookup = NULL;
+ const char *probe_name = NULL, *provider_name = NULL;
+ int ret = 0;
+ int fd, i;
+
+ assert(lttng_userspace_probe_location_get_type(probe_location) ==
+ LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT);
+
+ lookup = lttng_userspace_probe_location_get_lookup_method(probe_location);
+ if (!lookup) {
+ ret = -1;
+ goto end;
+ }
+
+ lookup_method_type =
+ lttng_userspace_probe_location_lookup_method_get_type(lookup);
+
+ assert(lookup_method_type ==
+ LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT);
+
+
+ probe_name = lttng_userspace_probe_location_tracepoint_get_probe_name(
+ probe_location);
+ if (!probe_name) {
+ ret = -1;
+ goto end;
+ }
+
+ provider_name = lttng_userspace_probe_location_tracepoint_get_provider_name(
+ probe_location);
+ if (!provider_name) {
+ ret = -1;
+ goto end;
+ }
+
+ fd = lttng_userspace_probe_location_tracepoint_get_binary_fd(probe_location);
+ if (fd < 0) {
+ ret = -1;
+ goto end;
+ }
+
+ ret = run_as_extract_sdt_probe_offsets(fd, provider_name, probe_name,
+ uid, gid, offsets, offsets_count);
+ if (ret < 0) {
+ DBG("userspace probe offset calculation failed for sdt "
+ "probe %s:%s", provider_name, probe_name);
+ goto end;
+ }
+
+ if (*offsets_count == 0) {
+ DBG("no userspace probe offset found");
+ goto end;
+ }
+
+ DBG("%u userspace probe SDT offsets found for %s:%s at:",
+ *offsets_count, provider_name, probe_name);
+ for (i = 0; i < *offsets_count; i++) {
+ DBG("\t0x%jd", (intmax_t)((*offsets)[i]));
+ }
+end:
+ return ret;
+}
+
+static
+int userspace_probe_add_callsite(
+ const struct lttng_userspace_probe_location *location,
+ uid_t uid, gid_t gid, int fd)
+{
+ const struct lttng_userspace_probe_location_lookup_method *lookup_method = NULL;
+ enum lttng_userspace_probe_location_lookup_method_type type;
+ int ret;
+
+ lookup_method = lttng_userspace_probe_location_get_lookup_method(location);
+ if (!lookup_method) {
+ ret = -1;
+ goto end;
+ }
+
+ type = lttng_userspace_probe_location_lookup_method_get_type(lookup_method);
+ switch (type) {
+ case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF:
+ {
+ struct lttng_kernel_event_callsite callsite;
+ uint64_t offset;
+
+ ret = extract_userspace_probe_offset_function_elf(location,
+ uid, gid, &offset);
+ if (ret) {
+ ret = LTTNG_ERR_PROBE_LOCATION_INVAL;
+ goto end;
+ }
+
+ callsite.u.uprobe.offset = offset;
+ ret = kernctl_add_callsite(fd, &callsite);
+ if (ret) {
+ WARN("Failed to add callsite to ELF userspace probe.");
+ ret = LTTNG_ERR_KERN_ENABLE_FAIL;
+ goto end;
+ }
+ break;
+ }
+ case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT:
+ {
+ int i;
+ uint64_t *offsets = NULL;
+ uint32_t offsets_count;
+ struct lttng_kernel_event_callsite callsite;
+
+ /*
+ * This call allocates the offsets buffer. This buffer must be freed
+ * by the caller
+ */
+ ret = extract_userspace_probe_offset_tracepoint_sdt(location,
+ uid, gid, &offsets, &offsets_count);
+ if (ret) {
+ ret = LTTNG_ERR_PROBE_LOCATION_INVAL;
+ goto end;
+ }
+ for (i = 0; i < offsets_count; i++) {
+ callsite.u.uprobe.offset = offsets[i];
+ ret = kernctl_add_callsite(fd, &callsite);
+ if (ret) {
+ WARN("Failed to add callsite to SDT userspace probe");
+ ret = LTTNG_ERR_KERN_ENABLE_FAIL;
+ free(offsets);
+ goto end;
+ }
+ }
+ free(offsets);
+ break;
+ }
+ default:
+ ret = LTTNG_ERR_PROBE_LOCATION_INVAL;
+ goto end;
+ }
+end:
+ return ret;
+}
+
+/*
+ * Extract the offsets of the instrumentation point for the different lookup
+ * methods.
+ */
+static
+int userspace_probe_event_add_callsites(struct lttng_event *ev,
+ struct ltt_kernel_session *session, int fd)
+{
+ int ret;
+ const struct lttng_userspace_probe_location *location = NULL;
+
+ assert(ev);
+ assert(ev->type == LTTNG_EVENT_USERSPACE_PROBE);
+
+ location = lttng_event_get_userspace_probe_location(ev);
+ if (!location) {
+ ret = -1;
+ goto end;
+ }
+
+ ret = userspace_probe_add_callsite(location, session->uid, session->gid,
+ fd);
+ if (ret) {
+ WARN("Failed to add callsite to userspace probe event '%s'",
+ ev->name);
+ }
+
+end:
+ return ret;
+}
+
+/*
+ * Extract the offsets of the instrumentation point for the different look-up
+ * methods.
+ */
+static int userspace_probe_event_rule_add_callsites(
+ const struct lttng_event_rule *rule,
+ const struct lttng_credentials *creds,
+ int fd)
+{
+ int ret;
+ enum lttng_event_rule_status status;
+ enum lttng_event_rule_type event_rule_type;
+ const struct lttng_userspace_probe_location *location = NULL;
+
+ assert(rule);
+ assert(creds);
+
+ event_rule_type = lttng_event_rule_get_type(rule);
+ assert(event_rule_type == LTTNG_EVENT_RULE_TYPE_USERSPACE_PROBE);
+
+ status = lttng_event_rule_userspace_probe_get_location(rule, &location);
+ if (status != LTTNG_EVENT_RULE_STATUS_OK || !location) {
+ ret = -1;
+ goto end;
+ }
+
+ ret = userspace_probe_add_callsite(location,
+ lttng_credentials_get_uid(creds),
+ lttng_credentials_get_gid(creds), fd);
+ if (ret) {
+ WARN("Failed to add callsite to user space probe object: fd = %d",
+ fd);
+ }
+
+end:
+ return ret;
+}
+