|  | #!/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_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) | 
|  | 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 | 
|  |  | 
|  | UPGRADE = { | 
|  | 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()) |