summaryrefslogtreecommitdiffstats
path: root/debian/patches/bugfix/all/tools-perf-pmu-events-fix-reproducibility.patch
blob: bd1bcd490cbe208844fd2e8a6f69bcd1d52b9a91 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
From: Ben Hutchings <ben@decadent.org.uk>
Date: Sun, 25 Aug 2019 13:49:41 +0100
Subject: tools/perf: pmu-events: Fix reproducibility
Forwarded: https://lore.kernel.org/lkml/20190825131329.naqzd5kwg7mw5d3f@decadent.org.uk/T/#u

jevents.c uses nftw() to enumerate files and outputs the corresponding
C structs in the order they are found.  This makes it sensitive to
directory ordering, so that the perf executable is not reproducible.

To avoid this, store all the files and directories found and then sort
them by their (relative) path.  (This maintains the parent-first
ordering that nftw() promises.)  Then apply the existing callbacks to
them in the sorted order.

Don't both storing the stat buffers as we don't need them.

References: https://tests.reproducible-builds.org/debian/dbdtxt/bullseye/i386/linux_4.19.37-6.diffoscope.txt.gz
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
---
--- a/tools/perf/pmu-events/jevents.c
+++ b/tools/perf/pmu-events/jevents.c
@@ -51,6 +51,18 @@
 #include "json.h"
 #include "pmu-events.h"
 
+struct ordered_ftw_entry {
+	const char	*fpath;
+	int		typeflag;
+	struct FTW	ftwbuf;
+};
+
+struct ordered_ftw_state {
+	struct ordered_ftw_entry *entries;
+	size_t		n;
+	size_t		max;
+};
+
 int verbose;
 char *prog;
 
@@ -981,6 +993,79 @@ static int get_maxfds(void)
  */
 static FILE *eventsfp;
 static char *mapfile;
+static struct ordered_ftw_state *ordered_ftw_state;
+
+static int ordered_ftw_add(const char *fpath,
+			   const struct stat *sb __maybe_unused,
+			   int typeflag, struct FTW *ftwbuf)
+{
+	struct ordered_ftw_state *state = ordered_ftw_state;
+	struct ordered_ftw_entry *entry;
+
+	if (ftwbuf->level == 0 || ftwbuf->level > 3)
+		return 0;
+
+	/* Grow array if necessary */
+	if (state->n >= state->max) {
+		if (state->max == 0)
+			state->max = 16;
+		else
+			state->max *= 2;
+		state->entries = realloc(state->entries,
+					 state->max * sizeof(*state->entries));
+	}
+
+	entry = &state->entries[state->n++];
+	entry->fpath = strdup(fpath);
+	entry->typeflag = typeflag;
+	entry->ftwbuf = *ftwbuf;
+
+	return 0;
+}
+
+static int ordered_ftw_compare(const void *left, const void *right)
+{
+	const struct ordered_ftw_entry *left_entry = left;
+	const struct ordered_ftw_entry *right_entry = right;
+
+	return strcmp(left_entry->fpath, right_entry->fpath);
+}
+
+/*
+ * Wrapper for nftw() that iterates files in ASCII-order to ensure
+ * reproducible output
+ */
+static int ordered_ftw(const char *dirpath,
+		       int (*fn)(const char *, int, struct FTW *),
+		       int nopenfd, int flags)
+{
+	struct ordered_ftw_state state = { NULL, 0, 0 };
+	size_t i;
+	int rc;
+
+	ordered_ftw_state = &state;
+	rc = nftw(dirpath, ordered_ftw_add, nopenfd, flags);
+	if (rc)
+		goto out;
+
+	qsort(state.entries, state.n, sizeof(*state.entries),
+	      ordered_ftw_compare);
+
+	for (i = 0; i < state.n; i++) {
+		rc = fn(state.entries[i].fpath,
+			state.entries[i].typeflag,
+			&state.entries[i].ftwbuf);
+		if (rc)
+			goto out;
+	}
+
+out:
+	for (i = 0; i < state.n; i++)
+		free((char *)state.entries[i].fpath);
+	free(state.entries);;
+
+	return rc;
+}
 
 static int is_leaf_dir(const char *fpath)
 {
@@ -1033,19 +1118,19 @@ static int is_json_file(const char *name
 	return 0;
 }
 
-static int preprocess_arch_std_files(const char *fpath, const struct stat *sb,
+static int preprocess_arch_std_files(const char *fpath,
 				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 json_events(fpath, save_arch_std_events, NULL);
 
 	return 0;
 }
 
-static int process_one_file(const char *fpath, const struct stat *sb,
+static int process_one_file(const char *fpath,
 			    int typeflag, struct FTW *ftwbuf)
 {
 	char *tblname, *bname;
@@ -1075,9 +1160,9 @@ static int process_one_file(const char *
 	} else
 		bname = (char *) fpath + ftwbuf->base;
 
-	pr_debug("%s %d %7jd %-20s %s\n",
+	pr_debug("%s %d %-20s %s\n",
 		 is_file ? "f" : is_dir ? "d" : "x",
-		 level, sb->st_size, bname, fpath);
+		 level, bname, fpath);
 
 	/* base dir or too deep */
 	if (level == 0 || level > 4)
@@ -1251,21 +1336,21 @@ int main(int argc, char *argv[])
 	 */
 
 	maxfds = get_maxfds();
-	rc = nftw(ldirname, preprocess_arch_std_files, maxfds, 0);
+	rc = ordered_ftw(ldirname, preprocess_arch_std_files, maxfds, 0);
 	if (rc)
 		goto err_processing_std_arch_event_dir;
 
-	rc = nftw(ldirname, process_one_file, maxfds, 0);
+	rc = ordered_ftw(ldirname, process_one_file, maxfds, 0);
 	if (rc)
 		goto err_processing_dir;
 
 	sprintf(ldirname, "%s/test", start_dirname);
 
-	rc = nftw(ldirname, preprocess_arch_std_files, maxfds, 0);
+	rc = ordered_ftw(ldirname, preprocess_arch_std_files, maxfds, 0);
 	if (rc)
 		goto err_processing_std_arch_event_dir;
 
-	rc = nftw(ldirname, process_one_file, maxfds, 0);
+	rc = ordered_ftw(ldirname, process_one_file, maxfds, 0);
 	if (rc)
 		goto err_processing_dir;