xref: /aosp_15_r20/external/perfetto/python/perfetto/trace_uri_resolver/util.py (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1# Copyright (C) 2022 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#      http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15import os
16from typing import Any, BinaryIO, Callable, Dict, List, Optional, Tuple
17
18# Limit parsing file to 1MB to maintain parity with the UI
19MAX_BYTES_LOADED = 1 * 1024 * 1024
20
21
22def file_generator(path: str):
23  with open(path, 'rb') as f:
24    yield from read_generator(f)
25
26
27def read_generator(trace: BinaryIO):
28  while True:
29    chunk = trace.read(MAX_BYTES_LOADED)
30    if not chunk:
31      break
32    yield chunk
33
34
35def merge_dicts(a: Dict[str, str], b: Dict[str, str]):
36  return {**a, **b}
37
38
39def parse_trace_uri(uri: str) -> Tuple[Optional[str], str]:
40  # This is definitely a path and not a URI
41  if uri.startswith('/') or uri.startswith('.'):
42    return None, uri
43
44  # If there's no colon, it cannot be a URI
45  idx = uri.find(':')
46  if idx == -1:
47    return None, uri
48
49  # If there is only a single character before the colon
50  # this is likely a Windows path; throw an error on other platforms
51  # to prevent single character names causing issues on Windows.
52  if idx == 1:
53    if os.name != 'nt':
54      raise Exception('Single character resolvers are not allowed')
55    return None, uri
56
57  return (uri[:idx], uri[idx + 1:])
58
59
60def to_list(cs: Any) -> Optional[List[Any]]:
61  """Converts input into list if it is not already a list.
62
63  For resolvers that can accept list types it may happen the user inputs just
64  a single value, to make the code generic enough we would want to convert those
65  input into list of single element. It does not do anything for None or List
66  types.
67  """
68  if cs is None or isinstance(cs, list):
69    return cs
70  return [cs]
71
72
73def _cs_list(cs: List[Any], fn: Callable[[Any], str], empty_default: str,
74             condition_sep: str) -> str:
75  """Converts list of constraints into list of clauses.
76
77  Applies function `fn` over each element in list `cs` and joins the list of
78  transformed strings with join string `condition_sep`. `empty_default` string
79  is returned incase cs is a list of length 0. 'TRUE' is returned when cs is
80  None
81
82  e.g.
83  Input:
84    cs: ['Android', 'Linux']
85    fn: "platform = '{}'".format
86    empty_default: FALSE
87    condition_sep: 'OR'
88  OUTPUT:
89    "(platform = 'Android' OR platform = 'Linux')"
90  """
91  if cs is None:
92    return 'TRUE'
93  if not cs:
94    return empty_default
95  return f'({condition_sep.join([fn(c) for c in cs])})'
96
97
98def and_list(cs: List[Any], fn: Callable[[Any], str],
99             empty_default: str) -> str:
100  """Converts list of constraints into list of AND clauses.
101
102  Function `fn` is applied over each element of list `cs` and joins the list of
103  transformed strings with ' AND ' string. `empty_default` string
104  is returned incase cs is a list of length 0. 'TRUE' is returned when cs is
105  None.
106
107  e.g.
108  Input:
109    cs: ['Android', 'Linux']
110    fn: "platform != '{}'".format
111    empty_default: FALSE
112  OUTPUT:
113    "(platform != 'Android' AND platform != 'Linux')"
114  """
115  return _cs_list(cs, fn, empty_default, ' AND ')
116
117
118def or_list(cs: List[Any], fn: Callable[[Any], str], empty_default: str) -> str:
119  """Converts list of constraints into list of OR clauses.
120
121  Similar to and_list method, just the join string is ' OR ' instead of ' AND '.
122
123  e.g.
124  Input:
125    cs: ['Android', 'Linux']
126    fn: "platform = '{}'".format
127    empty_default: FALSE
128  OUTPUT:
129    "(platform = 'Android' OR platform = 'Linux')"
130  """
131  return _cs_list(cs, fn, empty_default, ' OR ')
132