@@ -238,6 +238,30 @@ name of the process.
*LOG* or *LOG2* - bucket the key values in a log 2 values (1, 2, 3-4, 5-8, 9-16, 17-32, ...)
+The above fields are not case sensitive, and "LOG2" works as good as "log".
+
+A special CAST to _COUNTER_ or __COUNTER__ will make the field a value and not
+a key. For example:
+
+[source,c]
+--
+ SELECT common_pid, CAST(bytes_req AS _COUNTER_) FROM kmalloc
+--
+
+Which will create
+
+[source,c]
+--
+ echo 'hist:keys=common_pid:vals=bytes_req' > events/kmem/kmalloc/trigger
+
+ cat events/kmem/kmalloc/hist
+
+{ common_pid: 1812 } hitcount: 1 bytes_req: 32
+{ common_pid: 9111 } hitcount: 2 bytes_req: 272
+{ common_pid: 1768 } hitcount: 3 bytes_req: 1112
+{ common_pid: 0 } hitcount: 4 bytes_req: 512
+{ common_pid: 18297 } hitcount: 11 bytes_req: 2004
+--
RETURN VALUE
------------
@@ -88,6 +88,8 @@ int trace_append_filter(char **filter, unsigned int *state,
struct tracefs_synth *synth_init_from(struct tep_handle *tep,
const char *start_system,
const char *start_event);
+
+#define HIST_COUNTER_TYPE (TRACEFS_HIST_KEY_MAX + 100)
int synth_add_start_field(struct tracefs_synth *synth,
const char *start_field,
const char *name,
@@ -264,6 +264,7 @@ enum tracefs_hist_key_type {
TRACEFS_HIST_KEY_EXECNAME,
TRACEFS_HIST_KEY_LOG,
TRACEFS_HIST_KEY_USECS,
+ TRACEFS_HIST_KEY_MAX
};
enum tracefs_hist_sort_direction {
@@ -275,6 +276,8 @@ enum tracefs_hist_sort_direction {
#define TRACEFS_HIST_TIMESTAMP_USECS "common_timestamp.usecs"
#define TRACEFS_HIST_CPU "cpu"
+#define TRACEFS_HIST_COUNTER "__COUNTER__"
+
#define TRACEFS_HIST_HITCOUNT "hitcount"
struct tracefs_hist;
@@ -256,7 +256,7 @@ int tracefs_hist_add_key(struct tracefs_hist *hist, const char *key,
bool use_key = false;
char *key_type = NULL;
char **new_list;
- int ret;
+ int ret = -1;
switch (type) {
case TRACEFS_HIST_KEY_NORMAL:
@@ -284,6 +284,9 @@ int tracefs_hist_add_key(struct tracefs_hist *hist, const char *key,
case TRACEFS_HIST_KEY_USECS:
ret = asprintf(&key_type, "%s.usecs", key);
break;
+ case TRACEFS_HIST_KEY_MAX:
+ /* error */
+ break;
}
if (ret < 0)
@@ -1450,6 +1453,9 @@ tracefs_synth_get_start_hist(struct tracefs_synth *synth)
for (i = 0; keys[i]; i++) {
int type = types ? types[i] : 0;
+ if (type == HIST_COUNTER_TYPE)
+ continue;
+
key = keys[i];
if (i) {
@@ -1466,7 +1472,25 @@ tracefs_synth_get_start_hist(struct tracefs_synth *synth)
}
}
- if (hist && synth->start_filter) {
+ if (!hist)
+ return NULL;
+
+ for (i = 0; keys[i]; i++) {
+ int type = types ? types[i] : 0;
+
+ if (type != HIST_COUNTER_TYPE)
+ continue;
+
+ key = keys[i];
+
+ ret = tracefs_hist_add_value(hist, key);
+ if (ret < 0) {
+ tracefs_hist_free(hist);
+ return NULL;
+ }
+ }
+
+ if (synth->start_filter) {
hist->filter = strdup(synth->start_filter);
if (!hist->filter) {
tracefs_hist_free(hist);
@@ -1253,6 +1253,17 @@ static int verify_field_type(struct tep_handle *tep,
if (!type)
return -1;
+ if (!strcmp(type, TRACEFS_HIST_COUNTER) ||
+ !strcmp(type, "_COUNTER_")) {
+ ret = HIST_COUNTER_TYPE;
+ if (tfield->flags & (TEP_FIELD_IS_STRING | TEP_FIELD_IS_ARRAY)) {
+ parse_error(sb, field->raw,
+ "'%s' is a string, and counters may only be used with numbers\n");
+ ret = -1;
+ }
+ goto out;
+ }
+
for (i = 0; type[i]; i++)
type[i] = tolower(type[i]);
@@ -1292,6 +1303,7 @@ static int verify_field_type(struct tep_handle *tep,
field->raw, type);
ret = -1;
}
+ out:
free(type);
return ret;
fail_type:
@@ -1319,6 +1331,7 @@ static struct tracefs_synth *build_synth(struct tep_handle *tep,
const char *end_match;
bool started_start = false;
bool started_end = false;
+ bool non_val = false;
int ret;
if (!table->from)
@@ -1396,6 +1409,8 @@ static struct tracefs_synth *build_synth(struct tep_handle *tep,
type = verify_field_type(tep, table->sb, expr);
if (type < 0)
goto free;
+ if (type != HIST_COUNTER_TYPE)
+ non_val = true;
ret = synth_add_start_field(synth,
field->field, field->label,
type);
@@ -1426,6 +1441,14 @@ static struct tracefs_synth *build_synth(struct tep_handle *tep,
}
}
+ if (!non_val && !table->to) {
+ table->sb->line_no = 0;
+ table->sb->line_idx = 10;
+ parse_error(table->sb, "CAST",
+ "Not all SELECT items can be of type _COUNTER_\n");
+ goto free;
+ }
+
for (expr = table->where; expr; expr = expr->next) {
const char *filter_system = NULL;
const char *filter_event = NULL;