|
@@ -45,6 +45,7 @@
|
|
|
#include <sys/resource.h> /* getrlimit */
|
|
|
#include <ftw.h>
|
|
|
#include <sys/stat.h>
|
|
|
+#include <linux/list.h>
|
|
|
#include "jsmn.h"
|
|
|
#include "json.h"
|
|
|
#include "jevents.h"
|
|
@@ -351,6 +352,81 @@ static int print_events_table_entry(void *data, char *name, char *event,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+struct event_struct {
|
|
|
+ struct list_head list;
|
|
|
+ char *name;
|
|
|
+ char *event;
|
|
|
+ char *desc;
|
|
|
+ char *long_desc;
|
|
|
+ char *pmu;
|
|
|
+ char *unit;
|
|
|
+ char *perpkg;
|
|
|
+ char *metric_expr;
|
|
|
+ char *metric_name;
|
|
|
+ char *metric_group;
|
|
|
+};
|
|
|
+
|
|
|
+#define ADD_EVENT_FIELD(field) do { if (field) { \
|
|
|
+ es->field = strdup(field); \
|
|
|
+ if (!es->field) \
|
|
|
+ goto out_free; \
|
|
|
+} } while (0)
|
|
|
+
|
|
|
+#define FREE_EVENT_FIELD(field) free(es->field)
|
|
|
+
|
|
|
+#define TRY_FIXUP_FIELD(field) do { if (es->field && !*field) {\
|
|
|
+ *field = strdup(es->field); \
|
|
|
+ if (!*field) \
|
|
|
+ return -ENOMEM; \
|
|
|
+} } while (0)
|
|
|
+
|
|
|
+#define FOR_ALL_EVENT_STRUCT_FIELDS(op) do { \
|
|
|
+ op(name); \
|
|
|
+ op(event); \
|
|
|
+ op(desc); \
|
|
|
+ op(long_desc); \
|
|
|
+ op(pmu); \
|
|
|
+ op(unit); \
|
|
|
+ op(perpkg); \
|
|
|
+ op(metric_expr); \
|
|
|
+ op(metric_name); \
|
|
|
+ op(metric_group); \
|
|
|
+} while (0)
|
|
|
+
|
|
|
+static LIST_HEAD(arch_std_events);
|
|
|
+
|
|
|
+static void free_arch_std_events(void)
|
|
|
+{
|
|
|
+ struct event_struct *es, *next;
|
|
|
+
|
|
|
+ list_for_each_entry_safe(es, next, &arch_std_events, list) {
|
|
|
+ FOR_ALL_EVENT_STRUCT_FIELDS(FREE_EVENT_FIELD);
|
|
|
+ list_del(&es->list);
|
|
|
+ free(es);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static int save_arch_std_events(void *data, char *name, char *event,
|
|
|
+ char *desc, char *long_desc, char *pmu,
|
|
|
+ char *unit, char *perpkg, char *metric_expr,
|
|
|
+ char *metric_name, char *metric_group)
|
|
|
+{
|
|
|
+ struct event_struct *es;
|
|
|
+ struct stat *sb = data;
|
|
|
+
|
|
|
+ es = malloc(sizeof(*es));
|
|
|
+ if (!es)
|
|
|
+ return -ENOMEM;
|
|
|
+ memset(es, 0, sizeof(*es));
|
|
|
+ FOR_ALL_EVENT_STRUCT_FIELDS(ADD_EVENT_FIELD);
|
|
|
+ list_add_tail(&es->list, &arch_std_events);
|
|
|
+ return 0;
|
|
|
+out_free:
|
|
|
+ FOR_ALL_EVENT_STRUCT_FIELDS(FREE_EVENT_FIELD);
|
|
|
+ free(es);
|
|
|
+ return -ENOMEM;
|
|
|
+}
|
|
|
+
|
|
|
static void print_events_table_suffix(FILE *outfp)
|
|
|
{
|
|
|
fprintf(outfp, "{\n");
|
|
@@ -392,6 +468,32 @@ static char *real_event(const char *name, char *event)
|
|
|
return event;
|
|
|
}
|
|
|
|
|
|
+static int
|
|
|
+try_fixup(const char *fn, char *arch_std, char **event, char **desc,
|
|
|
+ char **name, char **long_desc, char **pmu, char **filter,
|
|
|
+ char **perpkg, char **unit, char **metric_expr, char **metric_name,
|
|
|
+ char **metric_group, unsigned long long eventcode)
|
|
|
+{
|
|
|
+ /* try to find matching event from arch standard values */
|
|
|
+ struct event_struct *es;
|
|
|
+
|
|
|
+ list_for_each_entry(es, &arch_std_events, list) {
|
|
|
+ if (!strcmp(arch_std, es->name)) {
|
|
|
+ if (!eventcode && es->event) {
|
|
|
+ /* allow EventCode to be overridden */
|
|
|
+ free(*event);
|
|
|
+ *event = NULL;
|
|
|
+ }
|
|
|
+ FOR_ALL_EVENT_STRUCT_FIELDS(TRY_FIXUP_FIELD);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ pr_err("%s: could not find matching %s for %s\n",
|
|
|
+ prog, arch_std, fn);
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
/* Call func with each event in the json file */
|
|
|
int json_events(const char *fn,
|
|
|
int (*func)(void *data, char *name, char *event, char *desc,
|
|
@@ -427,6 +529,7 @@ int json_events(const char *fn,
|
|
|
char *metric_expr = NULL;
|
|
|
char *metric_name = NULL;
|
|
|
char *metric_group = NULL;
|
|
|
+ char *arch_std = NULL;
|
|
|
unsigned long long eventcode = 0;
|
|
|
struct msrmap *msr = NULL;
|
|
|
jsmntok_t *msrval = NULL;
|
|
@@ -512,6 +615,10 @@ int json_events(const char *fn,
|
|
|
addfield(map, &metric_expr, "", "", val);
|
|
|
for (s = metric_expr; *s; s++)
|
|
|
*s = tolower(*s);
|
|
|
+ } else if (json_streq(map, field, "ArchStdEvent")) {
|
|
|
+ addfield(map, &arch_std, "", "", val);
|
|
|
+ for (s = arch_std; *s; s++)
|
|
|
+ *s = tolower(*s);
|
|
|
}
|
|
|
/* ignore unknown fields */
|
|
|
}
|
|
@@ -536,8 +643,21 @@ int json_events(const char *fn,
|
|
|
if (name)
|
|
|
fixname(name);
|
|
|
|
|
|
+ if (arch_std) {
|
|
|
+ /*
|
|
|
+ * An arch standard event is referenced, so try to
|
|
|
+ * fixup any unassigned values.
|
|
|
+ */
|
|
|
+ err = try_fixup(fn, arch_std, &event, &desc, &name,
|
|
|
+ &long_desc, &pmu, &filter, &perpkg,
|
|
|
+ &unit, &metric_expr, &metric_name,
|
|
|
+ &metric_group, eventcode);
|
|
|
+ if (err)
|
|
|
+ goto free_strings;
|
|
|
+ }
|
|
|
err = func(data, name, real_event(name, event), desc, long_desc,
|
|
|
pmu, unit, perpkg, metric_expr, metric_name, metric_group);
|
|
|
+free_strings:
|
|
|
free(event);
|
|
|
free(desc);
|
|
|
free(name);
|
|
@@ -550,6 +670,8 @@ int json_events(const char *fn,
|
|
|
free(metric_expr);
|
|
|
free(metric_name);
|
|
|
free(metric_group);
|
|
|
+ free(arch_std);
|
|
|
+
|
|
|
if (err)
|
|
|
break;
|
|
|
tok += j;
|
|
@@ -777,6 +899,32 @@ static int is_leaf_dir(const char *fpath)
|
|
|
return res;
|
|
|
}
|
|
|
|
|
|
+static int is_json_file(const char *name)
|
|
|
+{
|
|
|
+ const char *suffix;
|
|
|
+
|
|
|
+ if (strlen(name) < 5)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ suffix = name + strlen(name) - 5;
|
|
|
+
|
|
|
+ if (strncmp(suffix, ".json", 5) == 0)
|
|
|
+ return 1;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int preprocess_arch_std_files(const char *fpath, const struct stat *sb,
|
|
|
+ int typeflag, struct FTW *ftwbuf)
|
|
|
+{
|
|
|
+ int level = ftwbuf->level;
|
|
|
+ int is_file = typeflag == FTW_F;
|
|
|
+
|
|
|
+ if (level == 1 && is_file && is_json_file(fpath))
|
|
|
+ return json_events(fpath, save_arch_std_events, (void *)sb);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int process_one_file(const char *fpath, const struct stat *sb,
|
|
|
int typeflag, struct FTW *ftwbuf)
|
|
|
{
|
|
@@ -854,9 +1002,7 @@ static int process_one_file(const char *fpath, const struct stat *sb,
|
|
|
* ignore it. It could be a readme.txt for instance.
|
|
|
*/
|
|
|
if (is_file) {
|
|
|
- char *suffix = bname + strlen(bname) - 5;
|
|
|
-
|
|
|
- if (strncmp(suffix, ".json", 5)) {
|
|
|
+ if (!is_json_file(bname)) {
|
|
|
pr_info("%s: Ignoring file without .json suffix %s\n", prog,
|
|
|
fpath);
|
|
|
return 0;
|
|
@@ -962,12 +1108,26 @@ int main(int argc, char *argv[])
|
|
|
|
|
|
maxfds = get_maxfds();
|
|
|
mapfile = NULL;
|
|
|
+ rc = nftw(ldirname, preprocess_arch_std_files, maxfds, 0);
|
|
|
+ if (rc && verbose) {
|
|
|
+ pr_info("%s: Error preprocessing arch standard files %s\n",
|
|
|
+ prog, ldirname);
|
|
|
+ goto empty_map;
|
|
|
+ } else if (rc < 0) {
|
|
|
+ /* Make build fail */
|
|
|
+ free_arch_std_events();
|
|
|
+ return 1;
|
|
|
+ } else if (rc) {
|
|
|
+ goto empty_map;
|
|
|
+ }
|
|
|
+
|
|
|
rc = nftw(ldirname, process_one_file, maxfds, 0);
|
|
|
if (rc && verbose) {
|
|
|
pr_info("%s: Error walking file tree %s\n", prog, ldirname);
|
|
|
goto empty_map;
|
|
|
} else if (rc < 0) {
|
|
|
/* Make build fail */
|
|
|
+ free_arch_std_events();
|
|
|
return 1;
|
|
|
} else if (rc) {
|
|
|
goto empty_map;
|
|
@@ -992,5 +1152,6 @@ int main(int argc, char *argv[])
|
|
|
empty_map:
|
|
|
fclose(eventsfp);
|
|
|
create_empty_mapping(output_file);
|
|
|
+ free_arch_std_events();
|
|
|
return 0;
|
|
|
}
|