| #!/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()) |