xref: /aosp_15_r20/external/perfetto/python/tools/update_permalink.py (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1#!/usr/bin/env python3
2
3import sys
4import argparse
5import urllib
6import json
7import urllib.request
8import urllib.parse
9import ssl
10import hashlib
11import copy
12
13BUCKET_NAME = "perfetto-ui-data"
14CURRENT_STATE_VERSION = 28
15
16
17def upgrade_15(old):
18  new = copy.deepcopy(old)
19  new["version"] = 16
20  new["flamegraphModalDismissed"] = False
21  return new
22
23
24def upgrade_16(old):
25  new = copy.deepcopy(old)
26  new["version"] = 17
27  new["nextId"] = max(old["nextId"], old["nextNoteId"], old["nextAreaId"])
28  engines = old["engines"]
29  if len(engines) > 0:
30    new["currentEngineId"] = list(engines.values())[0]['id']
31  return new
32
33
34def upgrade_17(old):
35  new = copy.deepcopy(old)
36  new["version"] = 18
37  # TODO(hjd): Update
38  return new
39
40
41def upgrade_18(old):
42  new = copy.deepcopy(old)
43  new["version"] = 19
44  # TODO(hjd): Update
45  return new
46
47
48def upgrade_19(old):
49  new = copy.deepcopy(old)
50  new["version"] = 20
51  # TODO(hjd): Update
52  return new
53
54
55def upgrade_20(old):
56  new = copy.deepcopy(old)
57  new["version"] = 21
58  # TODO(hjd): Update
59  return new
60
61
62def upgrade_20(old):
63  new = copy.deepcopy(old)
64  new["version"] = 22
65  # TODO(hjd): Update
66  return new
67
68
69def upgrade_21(old):
70  new = copy.deepcopy(old)
71  new["version"] = 22
72  # TODO(hjd): Update
73  return new
74
75
76def upgrade_22(old):
77  new = copy.deepcopy(old)
78  new["version"] = 23
79  new["logFilteringCriteria"] = {
80      "minimumLevel": 2,
81  }
82  return new
83
84
85def upgrade_23(old):
86  new = copy.deepcopy(old)
87  new["version"] = 24
88  current_engine_id = new["currentEngineId"]
89  new["engine"] = new["engines"][
90      current_engine_id] if current_engine_id else None
91  del new["currentEngineId"]
92  del new["engines"]
93  return new
94
95
96def upgrade_24(old):
97  new = copy.deepcopy(old)
98  new["version"] = 25
99  new["omniboxState"] = new["frontendLocalState"]["omniboxState"]
100  del new["frontendLocalState"]["omniboxState"]
101  return new
102
103
104def upgrade_25(old):
105  new = copy.deepcopy(old)
106  new["version"] = 26
107  new["logFilteringCriteria"]["tags"] = []
108  return new
109
110
111def upgrade_26(old):
112  new = copy.deepcopy(old)
113  new["version"] = 27
114  new["logFilteringCriteria"]["textEntry"] = ""
115  return new
116
117
118def upgrade_27(old):
119  new = copy.deepcopy(old)
120  new["version"] = 28
121  new["logFilteringCriteria"]["hideNonMatching"] = False
122  return new
123
124
125def bug_compatible_hash_mangling(hash):
126  pairs = [hash[i:i + 2] for i in range(0, len(hash), 2)]
127  return ''.join([pair.removeprefix("0") for pair in pairs])
128
129
130def upload_state(state):
131  data = state.encode("utf-8")
132  hash = bug_compatible_hash_mangling(hashlib.sha256(data).hexdigest())
133
134  try:
135    get(make_state_url(hash))
136  except:
137    pass
138  else:
139    return hash
140
141  url = f"https://www.googleapis.com/upload/storage/v1/b/{BUCKET_NAME}/o?uploadType=media&name={hash}&predefinedAcl=publicRead"
142  request = urllib.request.Request(url, data=data)
143  request.add_header("Content-Type", "application/json; charset=utf-8")
144  response = urllib.request.urlopen(request)
145  return hash
146
147
148def make_state_url(id):
149  return f"https://storage.googleapis.com/{BUCKET_NAME}/{id}"
150
151
152def make_ui_url(id):
153  return f"https://ui.perfetto.dev/#!/?s={id}"
154
155
156def extract_state_uuid(url):
157  fragment = urllib.parse.urlparse(url).fragment
158  fragment = fragment.removeprefix("!/?")
159  return urllib.parse.parse_qs(fragment)["s"][0]
160
161
162def get(url):
163  context = ssl._create_unverified_context()
164  response = urllib.request.urlopen(url, context=context)
165  contents = response.read().decode()
166  return contents
167
168
169def post(url):
170  context = ssl._create_unverified_context()
171  response = urllib.request.urlopen(url, context=context)
172  contents = response.read().decode()
173  return contents
174
175
176def main():
177  parser = argparse.ArgumentParser()
178  parser.add_argument("permalink", help="Permalink you wish to update")
179  parser.add_argument(
180      "--target-version",
181      help=f"Target state version (default: {CURRENT_STATE_VERSION})",
182      default=CURRENT_STATE_VERSION)
183  parser.add_argument(
184      "--verbose", help=f"Show debug information", action="store_true")
185  args = parser.parse_args()
186
187  permalink_url = args.permalink
188  old_uuid = extract_state_uuid(permalink_url)
189  old_state_url = make_state_url(old_uuid)
190  old_state = get(old_state_url)
191  old_json = json.loads(old_state)
192
193  old_state_version = old_json["version"]
194  new_state_version = args.target_version
195
196  print(json.dumps(old_json, sort_keys=True, indent=4))
197
198  UPGRADE = {
199      15: upgrade_15,
200      16: upgrade_16,
201      17: upgrade_17,
202      18: upgrade_18,
203      19: upgrade_19,
204      20: upgrade_20,
205      21: upgrade_21,
206      22: upgrade_22,
207      23: upgrade_23,
208      24: upgrade_24,
209      25: upgrade_25,
210      26: upgrade_26,
211      27: upgrade_27,
212  }
213
214  new_json = old_json
215  for i in range(old_state_version, new_state_version):
216    new_json = UPGRADE[i](new_json)
217
218  new_state = json.dumps(new_json)
219  new_uuid = upload_state(new_state)
220  new_url = make_ui_url(new_uuid)
221  print(f"Your new permalink is accessible at:")
222  print(f"{new_url}")
223  return 0
224
225
226if __name__ == "__main__":
227  sys.exit(main())
228