@@ -42,7 +42,7 @@ with a detailed message on any type of parsing error, including fields that do n
to an event, or if the events or fields are not properly compared.
The example program below is a fully functional parser where it will create a synthetic
-event from a SQL syntax passed in via the command line or a file.
+event from a SQL syntax passed in via the command line or a file.
The SQL format is as follows:
@@ -364,11 +364,21 @@ $(bdir)/libtracefs.so.$(TRACEFS_VERSION): force
$(Q)mkdir -p $(bdir)
$(Q)$(MAKE) -C $(src)/src libtracefs.so
+$(bdir)/sqlhist.c: Documentation/libtracefs-sql.txt
+ cat $< | sed -ne '/^EXAMPLE/,/FILES/ { /EXAMPLE/,+2d ; /^FILES/d ; /^--/d ; p}' > $@
+
+$(bdir)/sqlhist.o: $(bdir)/sqlhist.c
+ $(CC) -g -Wall -c -o $@ $^ -Iinclude/ $(LIBTRACEEVENT_INCLUDES)
+
+sqlhist: $(bdir)/sqlhist.o $(LIBTRACEFS_STATIC)
+ $(CC) -o $@ $^ $(LIBTRACEEVENT_LIBS)
+
clean:
$(MAKE) -C $(src)/utest clean
$(MAKE) -C $(src)/src clean
$(RM) $(TARGETS) $(bdir)/*.a $(bdir)/*.so $(bdir)/*.so.* $(bdir)/*.o $(bdir)/.*.d
$(RM) $(PKG_CONFIG_FILE)
$(RM) $(VERSION_FILE)
+ $(RM) $(bdir)/sqlhist.o $(bdir)/sqlhist.c sqlhist
.PHONY: clean
@@ -1468,7 +1468,7 @@ tracefs_synth_get_start_hist(struct tracefs_synth *synth)
hist = tracefs_hist_alloc(tep, system, event,
key, type);
if (!hist)
- break;
+ return NULL;
}
}
@@ -36,7 +36,8 @@ enum alias_type {
struct field {
struct expr *next; /* private link list */
const char *system;
- const char *event;
+ const char *event_name;
+ struct tep_event *event;
const char *raw;
const char *label;
const char *field;
@@ -515,89 +516,175 @@ __hidden int table_start(struct sqlhist_bison *sb)
return 0;
}
-static int update_vars(struct sql_table *table, struct field *event)
+static int test_event_exists(struct tep_handle *tep,
+ struct sqlhist_bison *sb,
+ struct expr *expr, struct tep_event **pevent)
+{
+ struct field *field = &expr->field;
+ const char *system = field->system;
+ const char *event = field->event_name;
+
+ if (!field->event)
+ field->event = tep_find_event_by_name(tep, system, event);
+ if (pevent)
+ *pevent = field->event;
+
+ if (field->event)
+ return 0;
+
+ sb->line_no = expr->line;
+ sb->line_idx = expr->idx;
+
+ parse_error(sb, field->raw, "event not found\n");
+ return -1;
+}
+
+static int test_field_exists(struct tep_handle *tep,
+ struct sqlhist_bison *sb,
+ struct expr *expr)
+{
+ struct field *field = &expr->field;
+ struct tep_format_field *tfield;
+ char *field_name;
+ const char *p;
+
+ if (!field->event) {
+ if (test_event_exists(tep, sb, expr, NULL))
+ return -1;
+ }
+
+ /* The field could have a conversion */
+ p = strchr(field->field, '.');
+ if (p)
+ field_name = strndup(field->field, p - field->field);
+ else
+ field_name = strdup(field->field);
+
+ if (!field_name)
+ return -1;
+
+ if (!strcmp(field_name, TRACEFS_TIMESTAMP) ||
+ !strcmp(field->field, TRACEFS_TIMESTAMP_USECS))
+ tfield = (void *)1L;
+ else
+ tfield = tep_find_any_field(field->event, field_name);
+ free(field_name);
+
+ if (tfield)
+ return 0;
+
+ sb->line_no = expr->line;
+ sb->line_idx = expr->idx;
+
+ parse_error(sb, field->raw,
+ "Field '%s' not part of event %s\n",
+ field->field, field->event_name);
+ return -1;
+}
+
+static int update_vars(struct tep_handle *tep,
+ struct sql_table *table,
+ struct expr *expr)
{
struct sqlhist_bison *sb = table->sb;
- struct expr *expr;
+ struct field *event_field = &expr->field;
+ struct tep_event *event;
struct field *field;
const char *label;
- const char *p, *r;
- char *system;
- int len;
+ const char *raw = event_field->raw;
+ const char *event_name;
+ const char *system;
+ const char *p;
+ int label_len = 0, event_len, system_len;
- p = strchr(event->raw, '.');
+ p = strchr(raw, '.');
if (p) {
- system = strndup(event->raw, p - event->raw);
- if (!system)
+ char *str;
+
+ str = strndup(raw, p - raw);
+ if (!str)
return -1;
- event->system = store_str(sb, system);
- free(system);
- if (!event->system)
+ event_field->system = store_str(sb, str);
+ free(str);
+ if (!event_field->system)
return -1;
p++;
} else {
- p = event->raw;
+ p = raw;
}
- event->event = store_str(sb, p);
- if (!event->event)
+ event_field->event_name = store_str(sb, p);
+ if (!event_field->event_name)
+ return -1;
+
+ if (test_event_exists(tep, sb, expr, &event))
+ return -1;
+
+ if (!event_field->system)
+ event_field->system = store_str(sb, event->system);
+
+ if (!event_field->system)
return -1;
- if (!event->label)
- event->label = event->event;
+ label = event_field->label;
+ if (label)
+ label_len = strlen(label);
+
+ system = event_field->system;
+ system_len = strlen(system);
- label = event->label;
- len = strlen(label);
+ event_name = event_field->event_name;
+ event_len = strlen(event_name);
for_each_field(expr, field, table) {
+ int len;
+
field = &expr->field;
if (field->event)
continue;
- p = strchr(field->raw, '.');
- if (p) {
- /* Does this field have a system */
- r = strchr(p + 1, '.');
- if (r) {
- /* This has a system, and is not a alias */
- system = strndup(field->raw, p - field->raw);
- if (!system)
- return -1;
- field->system = store_str(sb, system);
- free(system);
- if (!field->system)
- return -1;
-
- /* save the event as well */
- p++;
- system = strndup(p, r - p);
- if (!system)
- return -1;
- field->event = store_str(sb, system);
- free(system);
- if (!field->event)
- return -1;
- r++;
- field->field = store_str(sb, r);
- goto check_timestamps;
- }
+ raw = field->raw;
+
+ /*
+ * The field could be:
+ * system.event.field...
+ * event.field...
+ * label.field...
+ * We check label first.
+ */
+
+ len = label_len;
+ if (label && !strncmp(raw, label, len) &&
+ raw[len] == '.') {
+ /* Label matches and takes precedence */
+ goto found;
}
- if (strncmp(field->raw, label, len))
- continue;
+ if (!strncmp(raw, system, system_len) &&
+ raw[system_len] == '.') {
+ raw += system_len + 1;
+ /* Check the event portion next */
+ }
- if (field->raw[len] != '.')
+ len = event_len;
+ if (strncmp(raw, event_name, len) ||
+ raw[len] != '.') {
+ /* Does not match */
continue;
+ }
+ found:
+ field->system = system;
+ field->event_name = event_name;
+ field->event = event;
+ field->field = raw + len + 1;
- field->system = event->system;
- field->event = event->event;
- field->field = field->raw + len + 1;
- check_timestamps:
if (!strcmp(field->field, "TIMESTAMP"))
field->field = store_str(sb, TRACEFS_TIMESTAMP);
- else if (!strcmp(field->field, "TIMESTAMP_USECS"))
+ if (!strcmp(field->field, "TIMESTAMP_USECS"))
field->field = store_str(sb, TRACEFS_TIMESTAMP_USECS);
+ if (test_field_exists(tep, sb, expr))
+ return -1;
}
return 0;
@@ -608,29 +695,29 @@ static int update_vars(struct sql_table *table, struct field *event)
* selections can be fields and not mention the event itself.
*/
static int update_fields(struct tep_handle *tep,
- struct sql_table *table, struct field *event_field)
+ struct sql_table *table,
+ struct expr *expr)
{
+ struct field *event_field = &expr->field;
struct sqlhist_bison *sb = table->sb;
struct tep_format_field *tfield;
struct tep_event *event;
- struct expr *expr;
struct field *field;
const char *p;
int len;
- /* First update fields with aliases an such */
- update_vars(table, event_field);
+ /* First update fields with aliases an such and add event */
+ update_vars(tep, table, expr);
- /* The update_vars already updated event->system and event->event */
- event = tep_find_event_by_name(tep, event_field->system,
- event_field->event);
/*
* If event is not found, the creation of the synth will
* add a proper error, so return "success".
*/
- if (!event)
+ if (!event_field->event)
return 0;
+ event = event_field->event;
+
for_each_field(expr, field, table) {
const char *field_name;
@@ -659,7 +746,8 @@ static int update_fields(struct tep_handle *tep,
continue;
field->system = event_field->system;
- field->event = event_field->event;
+ field->event_name = event_field->event_name;
+ field->event = event;
field->field = field_name;
}
@@ -746,7 +834,7 @@ static void assign_match(const char *system, const char *event,
rval = &match->rval->field;
if (lval->system == system &&
- lval->event == event) {
+ lval->event_name == event) {
*start_match = lval->field;
*end_match = rval->field;
} else {
@@ -772,7 +860,7 @@ static int build_compare(struct tracefs_synth *synth,
rval = &compare->rval->field;
if (lval->system == system &&
- lval->event == event) {
+ lval->event_name == event) {
start_field = lval->field;
end_field = rval->field;
calc = TRACEFS_SYNTH_DELTA_START;
@@ -829,12 +917,12 @@ static int do_verify_filter(struct sqlhist_bison *sb, struct filter *filter,
*/
if (!*system && !*event) {
*system = filter->lval->field.system;
- *event = filter->lval->field.event;
+ *event = filter->lval->field.event_name;
return 0;
}
if (filter->lval->field.system != *system ||
- filter->lval->field.event != *event)
+ filter->lval->field.event_name != *event)
return verify_filter_error(sb, filter->lval, *event);
return 0;
@@ -1035,45 +1123,6 @@ static int build_filter(struct tep_handle *tep, struct sqlhist_bison *sb,
return ret;
}
-static int test_event_exists(struct tep_handle *tep, struct sqlhist_bison *sb,
- struct expr *expr, struct tep_event **pevent)
-{
- const char *system = expr->field.system;
- const char *event_name = expr->field.event;
- struct tep_event *event;
-
- event = tep_find_event_by_name(tep, system, event_name);
- if (pevent)
- *pevent = event;
- if (event)
- return 0;
-
- sb->line_no = expr->line;
- sb->line_idx = expr->idx;
-
- parse_error(sb, expr->field.raw, "event not found\n");
- return -1;
-}
-
-static int test_field_exists(struct tep_handle *tep, struct sqlhist_bison *sb,
- struct expr *expr)
-{
- struct tep_event *event;
-
- if (test_event_exists(tep, sb, expr, &event))
- return -1;
-
- if (trace_verify_event_field(event, expr->field.field, NULL))
- return 0;
-
- sb->line_no = expr->line;
- sb->line_idx = expr->idx;
-
- parse_error(sb, expr->field.raw, "Field '%s' not part of event %s\n",
- expr->field.field, expr->field.event);
- return -1;
-}
-
static void *field_match_error(struct tep_handle *tep, struct sqlhist_bison *sb,
struct match *match)
{
@@ -1233,11 +1282,11 @@ static int verify_field_type(struct tep_handle *tep,
sb->line_no = expr->line;
sb->line_idx = expr->idx;
- event = tep_find_event_by_name(tep, field->system, field->event);
+ event = tep_find_event_by_name(tep, field->system, field->event_name);
if (!event) {
parse_error(sb, field->raw,
"Event '%s' not found\n",
- field->event ? : "(null)");
+ field->event_name ? : "(null)");
return -1;
}
@@ -1339,12 +1388,12 @@ static struct tracefs_synth *build_synth(struct tep_handle *tep,
/* This could be a simple SQL statement to only build a histogram */
if (!table->to) {
- ret = update_fields(tep, table, &table->from->field);
+ ret = update_fields(tep, table, table->from);
if (ret < 0)
return NULL;
start_system = table->from->field.system;
- start_event = table->from->field.event;
+ start_event = table->from->field.event_name;
synth = synth_init_from(tep, start_system, start_event);
if (!synth)
@@ -1352,12 +1401,16 @@ static struct tracefs_synth *build_synth(struct tep_handle *tep,
goto hist_only;
}
- ret = update_vars(table, &table->from->field);
+ ret = update_vars(tep, table, table->from);
+ if (ret < 0)
+ return NULL;
+
+ ret = update_vars(tep, table, table->to);
if (ret < 0)
return NULL;
start_system = table->from->field.system;
- start_event = table->from->field.event;
+ start_event = table->from->field.event_name;
match = table->matches;
if (!match)
@@ -1368,7 +1421,7 @@ static struct tracefs_synth *build_synth(struct tep_handle *tep,
return NULL;
end_system = table->to->field.system;
- end_event = table->to->field.event;
+ end_event = table->to->field.event_name;
assign_match(start_system, start_event, match,
&start_match, &end_match);
@@ -1404,7 +1457,7 @@ static struct tracefs_synth *build_synth(struct tep_handle *tep,
ret = -1;
field = &expr->field;
if (field->system == start_system &&
- field->event == start_event) {
+ field->event_name == start_event) {
int type;
type = verify_field_type(tep, table->sb, expr);
if (type < 0)
@@ -51,7 +51,7 @@
#define SQL_2_SQL "select woke.next_pid as woke_pid, wake.common_pid as waking_pid from sched_waking as wake join sched_switch as woke on woke.next_pid = wake.pid"
#define SQL_3_EVENT "wakeup_lat"
-#define SQL_3_SQL "select start.pid, end.next_prio as prio, (end.TIMESTAMP_USECS - start.TIMESTAMP_USECS) as lat from sched_waking as start join sched_switch as end on start.pid = end.next_pid"
+#define SQL_3_SQL "select sched_switch.next_prio as prio, end.prev_prio as pprio, (sched.sched_waking.common_timestamp.usecs - end.TIMESTAMP_USECS) as lat from sched_waking as start join sched_switch as end on start.pid = end.next_pid"
#define SQL_4_EVENT "wakeup_lat_2"
#define SQL_4_SQL "select start.pid, end.next_prio as prio, (end.TIMESTAMP_USECS - start.TIMESTAMP_USECS) as lat from sched_waking as start join sched_switch as end on start.pid = end.next_pid where (start.prio >= 1 && start.prio < 100) || !(start.pid >= 0 && start.pid <= 1) && end.prev_pid != 0"