diff --git a/debian/changelog b/debian/changelog index 6e0d93888d7f..f48ede9ca8a3 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +linux (5.3~rc5-1~exp2) UNRELEASED; urgency=medium + + * tools/perf: pmu-events: Fix reproducibility + + -- Ben Hutchings Sun, 25 Aug 2019 14:14:46 +0100 + linux (5.3~rc5-1~exp1) experimental; urgency=medium * New upstream release candidate diff --git a/debian/patches/bugfix/all/tools-perf-pmu-events-fix-reproducibility.patch b/debian/patches/bugfix/all/tools-perf-pmu-events-fix-reproducibility.patch new file mode 100644 index 000000000000..66366eb08afd --- /dev/null +++ b/debian/patches/bugfix/all/tools-perf-pmu-events-fix-reproducibility.patch @@ -0,0 +1,165 @@ +From: Ben Hutchings +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 +--- +--- a/tools/perf/pmu-events/jevents.c ++++ b/tools/perf/pmu-events/jevents.c +@@ -50,6 +50,12 @@ + #include "json.h" + #include "jevents.h" + ++struct found_file { ++ const char *fpath; ++ int typeflag; ++ struct FTW ftwbuf; ++}; ++ + int verbose; + char *prog; + +@@ -865,6 +871,44 @@ static int get_maxfds(void) + * nftw() doesn't let us pass an argument to the processing function, + * so use a global variables. + */ ++static struct found_file *found_files; ++static size_t n_found_files; ++static size_t max_found_files; ++ ++static int add_one_file(const char *fpath, const struct stat *sb, ++ int typeflag, struct FTW *ftwbuf) ++{ ++ struct found_file *file; ++ ++ if (ftwbuf->level == 0 || ftwbuf->level > 3) ++ return 0; ++ ++ /* Grow array if necessary */ ++ if (n_found_files >= max_found_files) { ++ if (max_found_files == 0) ++ max_found_files = 16; ++ else ++ max_found_files *= 2; ++ found_files = realloc(found_files, ++ max_found_files * sizeof(*found_files)); ++ } ++ ++ file = &found_files[n_found_files++]; ++ file->fpath = strdup(fpath); ++ file->typeflag = typeflag; ++ file->ftwbuf = *ftwbuf; ++ ++ return 0; ++} ++ ++static int compare_files(const void *left, const void *right) ++{ ++ const struct found_file *left_file = left; ++ const struct found_file *right_file = right; ++ ++ return strcmp(left_file->fpath, right_file->fpath); ++} ++ + static FILE *eventsfp; + static char *mapfile; + +@@ -919,19 +963,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; +@@ -956,9 +1000,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 > 3) +@@ -1070,6 +1114,7 @@ int main(int argc, char *argv[]) + const char *output_file; + const char *start_dirname; + struct stat stbuf; ++ size_t i; + + prog = basename(argv[0]); + if (argc < 4) { +@@ -1113,8 +1158,26 @@ int main(int argc, char *argv[]) + */ + + maxfds = get_maxfds(); ++ rc = nftw(ldirname, add_one_file, maxfds, 0); ++ if (rc < 0) { ++ /* Make build fail */ ++ free_arch_std_events(); ++ return 1; ++ } else if (rc) { ++ goto empty_map; ++ } ++ ++ /* Sort file names to ensure reproduciblity */ ++ qsort(found_files, n_found_files, sizeof(*found_files), compare_files); ++ + mapfile = NULL; +- rc = nftw(ldirname, preprocess_arch_std_files, maxfds, 0); ++ for (i = 0; i < n_found_files; i++) { ++ rc = preprocess_arch_std_files(found_files[i].fpath, ++ found_files[i].typeflag, ++ &found_files[i].ftwbuf); ++ if (rc) ++ break; ++ } + if (rc && verbose) { + pr_info("%s: Error preprocessing arch standard files %s\n", + prog, ldirname); +@@ -1127,7 +1190,13 @@ int main(int argc, char *argv[]) + goto empty_map; + } + +- rc = nftw(ldirname, process_one_file, maxfds, 0); ++ for (i = 0; i < n_found_files; i++) { ++ rc = process_one_file(found_files[i].fpath, ++ found_files[i].typeflag, ++ &found_files[i].ftwbuf); ++ if (rc) ++ break; ++ } + if (rc && verbose) { + pr_info("%s: Error walking file tree %s\n", prog, ldirname); + goto empty_map; diff --git a/debian/patches/series b/debian/patches/series index 9aa8ff08beae..552c41ecedef 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -157,6 +157,7 @@ bugfix/all/tools-lib-api-fs-fs.c-fix-misuse-of-strncpy.patch bugfix/all/usbip-fix-misuse-of-strncpy.patch bugfix/x86/tools-x86_energy_perf_policy-fix-uninitialized-varia.patch bugfix/all/usbip-network-fix-unaligned-member-access.patch +bugfix/all/tools-perf-pmu-events-fix-reproducibility.patch # wireless: Disable regulatory.db direct loading (until we sort out signing) debian/wireless-disable-regulatory.db-direct-loading.patch