Merge benchmark data into single file for presentation

Change-Id: Ib609667e050745c3d3b67c0285dc7c5d25b3fc77
diff --git a/tools/historic_run.py b/tools/historic_run.py
index 35d12d9..5d6f8d5 100755
--- a/tools/historic_run.py
+++ b/tools/historic_run.py
@@ -56,11 +56,29 @@
     def __repr__(self):
         return self.__str__()
 
+    def hash(self):
+        return self.git_hash
+
+    def title(self):
+        result = subprocess.check_output(
+            ['git', 'show-branch', '--no-name', self.git_hash]).decode('utf-8')
+        return result.strip()
+
+    def author_name(self):
+        result = subprocess.check_output([
+            'git', 'show', '--no-notes', '--no-patch', '--pretty=%an',
+            self.git_hash
+        ]).decode('utf-8')
+        return result.strip()
+
+    def committer_timestamp(self):
+        return self.timestamp
+
 
 def git_commit_from_hash(hash):
     commit_timestamp = subprocess.check_output(
-        ['git', 'show', '--no-patch', '--no-notes', '--pretty=\'%ct\'',
-         hash]).decode().strip().strip('\'')
+        ['git', 'show', '--no-patch', '--no-notes', '--pretty=%ct',
+         hash]).decode('utf-8').strip()
     destination_dir = '%s/%s/' % (MASTER_COMMITS, hash)
     destination = '%s%s' % (destination_dir, 'r8.jar')
     commit = GitCommit(hash, destination_dir, destination, commit_timestamp)
diff --git a/tools/perf.py b/tools/perf.py
index 9e7a7b8..32d657a 100755
--- a/tools/perf.py
+++ b/tools/perf.py
@@ -94,18 +94,18 @@
         return json.loads(''.join(lines))
 
 
-def GetArtifactLocation(app, filename, options):
-    version = options.version or utils.get_HEAD_sha1()
-    return f'{app}/{options.target}/{version}/{filename}'
+def GetArtifactLocation(app, target, version, filename):
+    version_or_head = version or utils.get_HEAD_sha1()
+    return f'{app}/{target}/{version_or_head}/{filename}'
 
 
 def GetGSLocation(filename):
     return f'gs://{BUCKET}/{filename}'
 
 
-def ArchiveOutputFile(file, dest, options):
-    if options.outdir:
-        dest_in_outdir = os.path.join(options.outdir, dest)
+def ArchiveOutputFile(file, dest, outdir=None):
+    if outdir:
+        dest_in_outdir = os.path.join(outdir, dest)
         os.makedirs(os.path.dirname(dest_in_outdir), exist_ok=True)
         shutil.copyfile(file, dest_in_outdir)
     else:
@@ -126,7 +126,8 @@
                 if options.outdir:
                     raise NotImplementedError
                 output = GetGSLocation(
-                    GetArtifactLocation(app, 'result.json', options))
+                    GetArtifactLocation(app, options.target, options.version,
+                                        'result.json'))
                 if utils.cloud_storage_exists(output):
                     print(f'Skipping run, {output} already exists.')
                     continue
@@ -165,18 +166,20 @@
                 json.dump(
                     MergeBenchmarkResultJsonFiles(benchmark_result_json_files),
                     f)
-            ArchiveOutputFile(result_file,
-                              GetArtifactLocation(app, 'result.json', options),
-                              options)
+            ArchiveOutputFile(
+                result_file,
+                GetArtifactLocation(app, options.target, options.version,
+                                    'result.json'), options.outdir)
 
             # Write metadata.
             if os.environ.get('SWARMING_BOT_ID'):
                 meta_file = os.path.join(temp, "meta")
                 with open(meta_file, 'w') as f:
                     f.write("Produced by: " + os.environ.get('SWARMING_BOT_ID'))
-                ArchiveOutputFile(meta_file,
-                                  GetArtifactLocation(app, 'meta', options),
-                                  options)
+                ArchiveOutputFile(
+                    meta_file,
+                    GetArtifactLocation(app, options.target, options.version,
+                                        'meta'), options.outdir)
 
 
 if __name__ == '__main__':
diff --git a/tools/upload_benchmark_data_to_google_storage.py b/tools/upload_benchmark_data_to_google_storage.py
new file mode 100755
index 0000000..02b46e2
--- /dev/null
+++ b/tools/upload_benchmark_data_to_google_storage.py
@@ -0,0 +1,70 @@
+#!/usr/bin/env python3
+# Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+import historic_run
+import json
+import os
+import perf
+import utils
+
+import sys
+
+APPS = ['NowInAndroidApp', 'TiviApp']
+TARGETS = ['r8-full']
+NUM_COMMITS = 1000
+
+
+def ParseJsonFromCloudStorage(filename):
+    gs_location = perf.GetGSLocation(filename)
+    if not utils.file_exists_on_cloud_storage(gs_location):
+        return None
+    content = utils.cat_file_on_cloud_storage(gs_location)
+    try:
+        return json.loads(''.join(content))
+    except:
+        return None
+
+
+def main():
+    if utils.get_HEAD_branch() != 'main':
+        print('Expected to be on branch \'main\'')
+        sys.exit(1)
+
+    # Get the N most recent commits sorted by newest first.
+    top = utils.get_HEAD_sha1()
+    bottom = utils.get_nth_sha1_from_HEAD(NUM_COMMITS - 1)
+    commits = historic_run.enumerate_git_commits(top, bottom)
+    assert len(commits) == NUM_COMMITS
+
+    # Aggregate all the result.json files into a single benchmark_data.json file
+    # that has the same format as tools/perf/benchmark_data.json.
+    benchmark_data = []
+    for commit in commits:
+        benchmarks = {}
+        for app in APPS:
+            for target in TARGETS:
+                filename = perf.GetArtifactLocation(app, target, commit.hash(),
+                                                    'result.json')
+                app_benchmark_data = ParseJsonFromCloudStorage(filename)
+                if app_benchmark_data:
+                    benchmarks[app] = app_benchmark_data
+        if len(benchmarks):
+            benchmark_data.append({
+                'author': commit.author_name(),
+                'hash': commit.hash(),
+                'submitted': commit.committer_timestamp(),
+                'title': commit.title(),
+                'benchmarks': benchmarks
+            })
+
+    with utils.TempDir() as temp:
+        benchmark_data_file = os.path.join(temp, 'benchmark_data.json')
+        with open(benchmark_data_file, 'w') as f:
+            json.dump(benchmark_data, f)
+        perf.ArchiveOutputFile(benchmark_data_file, 'benchmark_data.json')
+
+
+if __name__ == '__main__':
+    sys.exit(main())
diff --git a/tools/utils.py b/tools/utils.py
index 4bd6910..f483212 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -350,6 +350,13 @@
         subprocess.check_output(cmd)
 
 
+def get_nth_sha1_from_HEAD(n):
+    result = subprocess.check_output(
+        ['git', 'log', f'--skip={n}', '--max-count=1',
+         '--pretty=format:%H']).decode('utf-8')
+    return result.strip()
+
+
 def get_sha1(filename):
     sha1 = hashlib.sha1()
     with open(filename, 'rb') as f: