The stack has been created for storing func and dso from the callchain. The sample has been added to a specific thread. It first checks if the thread exists in the Thread class. Then it call _add_sample function which is responsible for appending a new entry to the samples list. Also callchain parsing and storing part is implemented. Moreover removed the comment from thread.
Showing the changes to tools/perf/scripts/python (1 of 1 file). Show all
Unified · Split
| @@ -40,6 +40,9 @@ CATEGORIES = None · 39 unmodified lines | |||
| 40 | # The product name is used by the profiler UI to show the Operating system and Processor. | 40 | # The product name is used by the profiler UI to show the Operating system and Processor. |
| 41 | PRODUCT = os.popen('uname -op').read().strip() | 41 | PRODUCT = os.popen('uname -op').read().strip() |
| 42 | 42 | ||
| 43 | # Here key = tid, value = Thread | ||
| 44 | tid_to_thread = dict() | ||
| 45 | |||
| 43 | # The category index is used by the profiler UI to show the color of the flame graph. | 46 | # The category index is used by the profiler UI to show the color of the flame graph. |
| 44 | USER_CATEGORY_INDEX = 0 | 47 | USER_CATEGORY_INDEX = 0 |
| 45 | KERNEL_CATEGORY_INDEX = 1 | 48 | KERNEL_CATEGORY_INDEX = 1 |
| @@ -153,6 +156,25 @@ class Thread: · 107 unmodified lines | |||
| 153 | )) | 156 | )) |
| 154 | return frame_id | 157 | return frame_id |
| 155 | 158 | ||
| 159 | def _add_sample(self, comm: str, stack: List[str], time_ms: Milliseconds) -> None: | ||
| 160 | """Add a timestamped stack trace sample to the thread builder. | ||
| 161 | Args: | ||
| 162 | comm: command-line (name) of the thread at this sample | ||
| 163 | stack: sampled stack frames. Root first, leaf last. | ||
| 164 | time_ms: timestamp of sample in milliseconds. | ||
| 165 | """ | ||
| 166 | # Ihreads may not set their names right after they are created. | ||
| 167 | # Instead, they might do it later. In such situations, to use the latest name they have set. | ||
| 168 | if self.comm != comm: | ||
| 169 | self.comm = comm | ||
| 170 | |||
| 171 | prefix_stack_id = reduce(lambda prefix_id, frame: self._intern_stack | ||
| 172 | (self._intern_frame(frame), prefix_id), stack, None) | ||
| 173 | if prefix_stack_id is not None: | ||
| 174 | self.samples.append(Sample(stack_id=prefix_stack_id, | ||
| 175 | time_ms=time_ms, | ||
| 176 | responsiveness=0)) | ||
| 177 | |||
| 156 | def _to_json_dict(self) -> Dict: | 178 | def _to_json_dict(self) -> Dict: |
| 157 | """Converts current Thread to GeckoThread JSON format.""" | 179 | """Converts current Thread to GeckoThread JSON format.""" |
| 158 | # Gecko profile format is row-oriented data as List[List], | 180 | # Gecko profile format is row-oriented data as List[List], |
| @@ -231,9 +253,36 @@ def process_event(param_dict: Dict) -> None: · 72 unmodified lines | |||
| 231 | if not start_time: | 253 | if not start_time: |
| 232 | start_time = time_stamp | 254 | start_time = time_stamp |
| 233 | 255 | ||
| 256 | # Parse and append the callchain of the current sample into a stack. | ||
| 257 | stack = [] | ||
| 258 | if param_dict['callchain']: | ||
| 259 | for call in param_dict['callchain']: | ||
| 260 | if 'sym' not in call: | ||
| 261 | continue | ||
| 262 | stack.append(f'{call["sym"]["name"]} (in {call["dso"]})') | ||
| 263 | if len(stack) != 0: | ||
| 264 | # Reverse the stack, as root come first and the leaf at the end. | ||
| 265 | stack = stack[::-1] | ||
| 266 | |||
| 267 | # During perf record if -g is not used, the callchain is not available. | ||
| 268 | # In that case, the symbol and dso are available in the event parameters. | ||
| 269 | else: | ||
| 270 | func = param_dict['symbol'] if 'symbol' in param_dict else '[unknown]' | ||
| 271 | dso = param_dict['dso'] if 'dso' in param_dict else '[unknown]' | ||
| 272 | stack.append(f'{func} (in {dso})') | ||
| 273 | |||
| 274 | # Add sample to the specific thread. | ||
| 275 | thread = tid_to_thread.get(tid) | ||
| 276 | if thread is None: | ||
| 277 | thread = Thread(comm=comm, pid=pid, tid=tid) | ||
| 278 | tid_to_thread[tid] = thread | ||
| 279 | thread._add_sample(comm=comm, stack=stack, time_ms=time_stamp) | ||
| 280 | |||
| 234 | # Trace_end runs at the end and will be used to aggregate | 281 | # Trace_end runs at the end and will be used to aggregate |
| 235 | # the data into the final json object and print it out to stdout. | 282 | # the data into the final json object and print it out to stdout. |
| 236 | def trace_end() -> None: | 283 | def trace_end() -> None: |
| 284 | threads = [thread._to_json_dict() for thread in tid_to_thread.values()] | ||
| 285 | |||
| 237 | # Schema: https://github.com/firefox-devtools/profiler/blob/53970305b51b9b472e26d7457fee1d66cd4e2737/src/types/gecko-profile.js#L305 | 286 | # Schema: https://github.com/firefox-devtools/profiler/blob/53970305b51b9b472e26d7457fee1d66cd4e2737/src/types/gecko-profile.js#L305 |
| 238 | gecko_profile_with_meta = { | 287 | gecko_profile_with_meta = { |
| 239 | "meta": { | 288 | "meta": { |
| @@ -252,8 +301,7 @@ def trace_end() -> None: · 12 unmodified lines | |||
| 252 | "markerSchema": [], | 301 | "markerSchema": [], |
| 253 | }, | 302 | }, |
| 254 | "libs": [], | 303 | "libs": [], |
| 255 | # threads will be implemented in later commits. | 304 | "threads": threads, |
| 256 | # "threads": threads, | ||
| 257 | "processes": [], | 305 | "processes": [], |
| 258 | "pausedRanges": [], | 306 | "pausedRanges": [], |
| 259 | } | 307 | } |