#!/usr/bin/env python3 import sys import argparse import urllib import json import urllib.request import urllib.parse import ssl import hashlib import copy BUCKET_NAME = "perfetto-ui-data" CURRENT_STATE_VERSION = 28 def upgrade_15(old): new = copy.deepcopy(old) new["version"] = 16 new["flamegraphModalDismissed"] = False return new def upgrade_16(old): new = copy.deepcopy(old) new["version"] = 17 new["nextId"] = max(old["nextId"], old["nextNoteId"], old["nextAreaId"]) engines = old["engines"] if len(engines) > 0: new["currentEngineId"] = list(engines.values())[0]['id'] return new def upgrade_17(old): new = copy.deepcopy(old) new["version"] = 18 # TODO(hjd): Update return new def upgrade_18(old): new = copy.deepcopy(old) new["version"] = 19 # TODO(hjd): Update return new def upgrade_19(old): new = copy.deepcopy(old) new["version"] = 20 # TODO(hjd): Update return new def upgrade_20(old): new = copy.deepcopy(old) new["version"] = 21 # TODO(hjd): Update return new def upgrade_20(old): new = copy.deepcopy(old) new["version"] = 22 # TODO(hjd): Update return new def upgrade_21(old): new = copy.deepcopy(old) new["version"] = 22 # TODO(hjd): Update return new def upgrade_22(old): new = copy.deepcopy(old) new["version"] = 23 new["logFilteringCriteria"] = { "minimumLevel": 2, } return new def upgrade_23(old): new = copy.deepcopy(old) new["version"] = 24 current_engine_id = new["currentEngineId"] new["engine"] = new["engines"][ current_engine_id] if current_engine_id else None del new["currentEngineId"] del new["engines"] return new def upgrade_24(old): new = copy.deepcopy(old) new["version"] = 25 new["omniboxState"] = new["frontendLocalState"]["omniboxState"] del new["frontendLocalState"]["omniboxState"] return new def upgrade_25(old): new = copy.deepcopy(old) new["version"] = 26 new["logFilteringCriteria"]["tags"] = [] return new def upgrade_26(old): new = copy.deepcopy(old) new["version"] = 27 new["logFilteringCriteria"]["textEntry"] = "" return new def upgrade_27(old): new = copy.deepcopy(old) new["version"] = 28 new["logFilteringCriteria"]["hideNonMatching"] = False return new def bug_compatible_hash_mangling(hash): pairs = [hash[i:i + 2] for i in range(0, len(hash), 2)] return ''.join([pair.removeprefix("0") for pair in pairs]) def upload_state(state): data = state.encode("utf-8") hash = bug_compatible_hash_mangling(hashlib.sha256(data).hexdigest()) try: get(make_state_url(hash)) except: pass else: return hash url = f"https://www.googleapis.com/upload/storage/v1/b/{BUCKET_NAME}/o?uploadType=media&name={hash}&predefinedAcl=publicRead" request = urllib.request.Request(url, data=data) request.add_header("Content-Type", "application/json; charset=utf-8") response = urllib.request.urlopen(request) return hash def make_state_url(id): return f"https://storage.googleapis.com/{BUCKET_NAME}/{id}" def make_ui_url(id): return f"https://ui.perfetto.dev/#!/?s={id}" def extract_state_uuid(url): fragment = urllib.parse.urlparse(url).fragment fragment = fragment.removeprefix("!/?") return urllib.parse.parse_qs(fragment)["s"][0] def get(url): context = ssl._create_unverified_context() response = urllib.request.urlopen(url, context=context) contents = response.read().decode() return contents def post(url): context = ssl._create_unverified_context() response = urllib.request.urlopen(url, context=context) contents = response.read().decode() return contents def main(): parser = argparse.ArgumentParser() parser.add_argument("permalink", help="Permalink you wish to update") parser.add_argument( "--target-version", help=f"Target state version (default: {CURRENT_STATE_VERSION})", default=CURRENT_STATE_VERSION) parser.add_argument( "--verbose", help=f"Show debug information", action="store_true") args = parser.parse_args() permalink_url = args.permalink old_uuid = extract_state_uuid(permalink_url) old_state_url = make_state_url(old_uuid) old_state = get(old_state_url) old_json = json.loads(old_state) old_state_version = old_json["version"] new_state_version = args.target_version print(json.dumps(old_json, sort_keys=True, indent=4)) UPGRADE = { 15: upgrade_15, 16: upgrade_16, 17: upgrade_17, 18: upgrade_18, 19: upgrade_19, 20: upgrade_20, 21: upgrade_21, 22: upgrade_22, 23: upgrade_23, 24: upgrade_24, 25: upgrade_25, 26: upgrade_26, 27: upgrade_27, } new_json = old_json for i in range(old_state_version, new_state_version): new_json = UPGRADE[i](new_json) new_state = json.dumps(new_json) new_uuid = upload_state(new_state) new_url = make_ui_url(new_uuid) print(f"Your new permalink is accessible at:") print(f"{new_url}") return 0 if __name__ == "__main__": sys.exit(main())