+static int parse_kernel_function_opts(const char *source,
+ struct lttng_kernel_function_location **location)
+{
+ int ret = 0;
+ int match;
+ char s_hex[19];
+ char name[LTTNG_SYMBOL_NAME_LEN];
+ char *symbol_name = NULL;
+ uint64_t offset;
+
+ /* Check for symbol+offset. */
+ match = sscanf(source,
+ "%" LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API
+ "[^'+']+%18s",
+ name, s_hex);
+ if (match == 2) {
+ if (*s_hex == '\0') {
+ ERR("Kernel function symbol offset is missing.");
+ goto error;
+ }
+
+ symbol_name = strndup(name, LTTNG_SYMBOL_NAME_LEN);
+ if (!symbol_name) {
+ PERROR("Failed to copy kernel function location symbol name.");
+ goto error;
+ }
+ offset = strtoul(s_hex, NULL, 0);
+
+ *location = lttng_kernel_function_location_symbol_create(
+ symbol_name, offset);
+ if (!*location) {
+ ERR("Failed to create symbol kernel function location.");
+ goto error;
+ }
+
+ goto end;
+ }
+
+ /* Check for symbol. */
+ if (isalpha(name[0]) || name[0] == '_') {
+ match = sscanf(source,
+ "%" LTTNG_SYMBOL_NAME_LEN_SCANF_IS_A_BROKEN_API
+ "s",
+ name);
+ if (match == 1) {
+ symbol_name = strndup(name, LTTNG_SYMBOL_NAME_LEN);
+ if (!symbol_name) {
+ ERR("Failed to copy kernel function location symbol name.");
+ goto error;
+ }
+
+ *location = lttng_kernel_function_location_symbol_create(
+ symbol_name, 0);
+ if (!*location) {
+ ERR("Failed to create symbol kernel function location.");
+ goto error;
+ }
+
+ goto end;
+ }
+ }
+
+ /* Check for address. */
+ match = sscanf(source, "%18s", s_hex);
+ if (match > 0) {
+ uint64_t address;
+
+ if (*s_hex == '\0') {
+ ERR("Invalid kernel function location address.");
+ goto error;
+ }
+
+ address = strtoul(s_hex, NULL, 0);
+ *location = lttng_kernel_function_location_address_create(address);
+ if (!*location) {
+ ERR("Failed to create symbol kernel function location.");
+ goto error;
+ }
+
+ goto end;
+ }
+
+error:
+ /* No match */
+ ret = -1;
+ *location = NULL;
+
+end:
+ free(symbol_name);
+ return ret;
+}
+
+static
+struct lttng_event_expr *ir_op_load_expr_to_event_expr(
+ const struct ir_load_expression *load_expr,
+ const char *capture_str)
+{
+ char *provider_name = NULL;
+ struct lttng_event_expr *event_expr = NULL;
+ const struct ir_load_expression_op *load_expr_op = load_expr->child;
+ const enum ir_load_expression_type load_expr_child_type =
+ load_expr_op->type;
+
+ switch (load_expr_child_type) {
+ case IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT:
+ case IR_LOAD_EXPRESSION_GET_CONTEXT_ROOT:
+ {
+ const char *field_name;
+
+ load_expr_op = load_expr_op->next;
+ assert(load_expr_op);
+ assert(load_expr_op->type == IR_LOAD_EXPRESSION_GET_SYMBOL);
+ field_name = load_expr_op->u.symbol;
+ assert(field_name);
+
+ event_expr = load_expr_child_type == IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT ?
+ lttng_event_expr_event_payload_field_create(field_name) :
+ lttng_event_expr_channel_context_field_create(field_name);
+ if (!event_expr) {
+ ERR("Failed to create %s event expression: field name = `%s`.",
+ load_expr_child_type == IR_LOAD_EXPRESSION_GET_PAYLOAD_ROOT ?
+ "payload field" : "channel context",
+ field_name);
+ goto error;
+ }
+
+ break;
+ }
+ case IR_LOAD_EXPRESSION_GET_APP_CONTEXT_ROOT:
+ {
+ const char *colon;
+ const char *type_name;
+ const char *field_name;
+
+ load_expr_op = load_expr_op->next;
+ assert(load_expr_op);
+ assert(load_expr_op->type == IR_LOAD_EXPRESSION_GET_SYMBOL);
+ field_name = load_expr_op->u.symbol;
+ assert(field_name);
+
+ /*
+ * The field name needs to be of the form PROVIDER:TYPE. We
+ * split it here.
+ */
+ colon = strchr(field_name, ':');
+ if (!colon) {
+ ERR("Invalid app-specific context field name: missing colon in `%s`.",
+ field_name);
+ goto error;
+ }
+
+ type_name = colon + 1;
+ if (*type_name == '\0') {
+ ERR("Invalid app-specific context field name: missing type name after colon in `%s`.",
+ field_name);
+ goto error;
+ }
+
+ provider_name = strndup(field_name, colon - field_name);
+ if (!provider_name) {
+ PERROR("Failed to allocate field name string");
+ goto error;
+ }
+
+ event_expr = lttng_event_expr_app_specific_context_field_create(
+ provider_name, type_name);
+ if (!event_expr) {
+ ERR("Failed to create app-specific context field event expression: provider name = `%s`, type name = `%s`",
+ provider_name, type_name);
+ goto error;
+ }
+
+ break;
+ }
+ default:
+ ERR("%s: unexpected load expr type %d.", __func__,
+ load_expr_op->type);
+ abort();
+ }
+
+ load_expr_op = load_expr_op->next;
+
+ /* There may be a single array index after that. */
+ if (load_expr_op->type == IR_LOAD_EXPRESSION_GET_INDEX) {
+ struct lttng_event_expr *index_event_expr;
+ const uint64_t index = load_expr_op->u.index;
+
+ index_event_expr = lttng_event_expr_array_field_element_create(event_expr, index);
+ if (!index_event_expr) {
+ ERR("Failed to create array field element event expression.");
+ goto error;
+ }
+
+ event_expr = index_event_expr;
+ load_expr_op = load_expr_op->next;
+ }
+
+ switch (load_expr_op->type) {
+ case IR_LOAD_EXPRESSION_LOAD_FIELD:
+ /*
+ * This is what we expect, IR_LOAD_EXPRESSION_LOAD_FIELD is
+ * always found at the end of the chain.
+ */
+ break;
+ case IR_LOAD_EXPRESSION_GET_SYMBOL:
+ ERR("While parsing expression `%s`: Capturing subfields is not supported.",
+ capture_str);
+ goto error;
+
+ default:
+ ERR("%s: unexpected load expression operator %s.", __func__,
+ ir_load_expression_type_str(load_expr_op->type));
+ abort();
+ }
+
+ goto end;
+
+error:
+ lttng_event_expr_destroy(event_expr);
+ event_expr = NULL;
+
+end:
+ free(provider_name);
+
+ return event_expr;
+}
+
+static
+struct lttng_event_expr *ir_op_load_to_event_expr(
+ const struct ir_op *ir, const char *capture_str)
+{
+ struct lttng_event_expr *event_expr = NULL;
+
+ assert(ir->op == IR_OP_LOAD);
+
+ switch (ir->data_type) {
+ case IR_DATA_EXPRESSION:
+ {
+ const struct ir_load_expression *ir_load_expr =
+ ir->u.load.u.expression;
+
+ event_expr = ir_op_load_expr_to_event_expr(
+ ir_load_expr, capture_str);
+ break;
+ }
+ default:
+ ERR("%s: unexpected data type: %s.", __func__,
+ ir_data_type_str(ir->data_type));
+ abort();
+ }
+
+ return event_expr;
+}
+
+static
+const char *ir_operator_type_human_str(enum ir_op_type op)
+{
+ const char *name;
+
+ switch (op) {
+ case IR_OP_BINARY:
+ name = "Binary";
+ break;
+ case IR_OP_UNARY:
+ name = "Unary";
+ break;
+ case IR_OP_LOGICAL:
+ name = "Logical";
+ break;
+ default:
+ abort();
+ }
+
+ return name;
+}
+
+static
+struct lttng_event_expr *ir_op_root_to_event_expr(const struct ir_op *ir,
+ const char *capture_str)
+{
+ struct lttng_event_expr *event_expr = NULL;
+
+ assert(ir->op == IR_OP_ROOT);
+ ir = ir->u.root.child;
+
+ switch (ir->op) {
+ case IR_OP_LOAD:
+ event_expr = ir_op_load_to_event_expr(ir, capture_str);
+ break;
+ case IR_OP_BINARY:
+ case IR_OP_UNARY:
+ case IR_OP_LOGICAL:
+ ERR("While parsing expression `%s`: %s operators are not allowed in capture expressions.",
+ capture_str,
+ ir_operator_type_human_str(ir->op));
+ break;
+ default:
+ ERR("%s: unexpected IR op type: %s.", __func__,
+ ir_op_type_str(ir->op));
+ abort();
+ }
+
+ return event_expr;
+}
+
+static
+void destroy_event_expr(void *ptr)
+{
+ lttng_event_expr_destroy(ptr);
+}
+
+struct parse_event_rule_res {
+ /* Owned by this. */
+ struct lttng_event_rule *er;
+
+ /* Array of `struct lttng_event_expr *` */
+ struct lttng_dynamic_pointer_array capture_descriptors;
+};
+
+static
+struct parse_event_rule_res parse_event_rule(int *argc, const char ***argv)