• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2019 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5""" Functions to write trace data in perfetto protobuf format.
6"""
7
8import collections
9
10import perfetto_proto_classes as proto
11
12CLOCK_BOOTTIME = 6
13CLOCK_TELEMETRY = 64
14
15
16def reset_global_state():
17  global _interned_categories_by_tid
18  global _interned_event_names_by_tid
19  global _next_sequence_id
20  global _sequence_ids
21
22  # Dicts of strings for interning.
23  # Note that each thread has its own interning index.
24  _interned_categories_by_tid = collections.defaultdict(dict)
25  _interned_event_names_by_tid = collections.defaultdict(dict)
26
27  # Trusted sequence ids from telemetry should not overlap with
28  # trusted sequence ids from other trace producers. Chrome assigns
29  # sequence ids incrementally starting from 1 and we expect all its ids
30  # to be well below 10000. Starting from 2^20 will give us enough
31  # confidence that it will not overlap.
32  _next_sequence_id = 1<<20
33  _sequence_ids = {}
34
35
36reset_global_state()
37
38
39def _get_sequence_id(tid):
40  global _sequence_ids
41  global _next_sequence_id
42  if tid not in _sequence_ids:
43    _sequence_ids[tid] = _next_sequence_id
44    _next_sequence_id += 1
45  return _sequence_ids[tid]
46
47
48def _intern_category(category, trace_packet, tid):
49  global _interned_categories_by_tid
50  categories = _interned_categories_by_tid[tid]
51  if category not in categories:
52    # note that interning indices start from 1
53    categories[category] = len(categories) + 1
54    if trace_packet.interned_data is None:
55      trace_packet.interned_data = proto.InternedData()
56    trace_packet.interned_data.event_category = proto.EventCategory()
57    trace_packet.interned_data.event_category.iid = categories[category]
58    trace_packet.interned_data.event_category.name = category
59  return categories[category]
60
61
62def _intern_event_name(event_name, trace_packet, tid):
63  global _interned_event_names_by_tid
64  event_names = _interned_event_names_by_tid[tid]
65  if event_name not in event_names:
66    # note that interning indices start from 1
67    event_names[event_name] = len(event_names) + 1
68    if trace_packet.interned_data is None:
69      trace_packet.interned_data = proto.InternedData()
70    trace_packet.interned_data.legacy_event_name = proto.LegacyEventName()
71    trace_packet.interned_data.legacy_event_name.iid = event_names[event_name]
72    trace_packet.interned_data.legacy_event_name.name = event_name
73  return event_names[event_name]
74
75
76def write_thread_descriptor_event(output, pid, tid, ts):
77  """Write the first event in a sequence.
78
79  Call this function before writing any other events.
80  Note that this function is NOT thread-safe.
81
82  Args:
83    output: a file-like object to write events into.
84    pid: process ID.
85    tid: thread ID.
86    ts: timestamp in microseconds.
87  """
88  thread_descriptor_packet = proto.TracePacket()
89  thread_descriptor_packet.trusted_packet_sequence_id = _get_sequence_id(tid)
90  thread_descriptor_packet.timestamp = int(ts * 1e3)
91  thread_descriptor_packet.timestamp_clock_id = CLOCK_TELEMETRY
92
93  thread_descriptor_packet.thread_descriptor = proto.ThreadDescriptor()
94  thread_descriptor_packet.thread_descriptor.pid = pid
95  # Thread ID from threading module doesn't fit into int32.
96  # But we don't need the exact thread ID, just some number to
97  # distinguish one thread from another. We assume that the last 31 bits
98  # will do for that purpose.
99  thread_descriptor_packet.thread_descriptor.tid = tid & 0x7FFFFFFF
100  thread_descriptor_packet.incremental_state_cleared = True;
101
102  proto.write_trace_packet(output, thread_descriptor_packet)
103
104
105def write_event(output, ph, category, name, ts, args, tid):
106  """Write a trace event.
107
108  Note that this function is NOT thread-safe.
109
110  Args:
111    output: a file-like object to write events into.
112    ph: phase of event.
113    category: category of event.
114    name: event name.
115    ts: timestamp in microseconds.
116    args: dict of arbitrary key-values to be stored as DebugAnnotations.
117    tid: thread ID.
118  """
119  packet = proto.TracePacket()
120  packet.trusted_packet_sequence_id = _get_sequence_id(tid)
121  packet.timestamp = int(ts * 1e3)
122  packet.timestamp_clock_id = CLOCK_TELEMETRY
123
124  packet.track_event = proto.TrackEvent()
125  packet.track_event.category_iids = [_intern_category(category, packet, tid)]
126  legacy_event = proto.LegacyEvent()
127  legacy_event.phase = ord(ph)
128  legacy_event.name_iid = _intern_event_name(name, packet, tid)
129  packet.track_event.legacy_event = legacy_event
130
131  for name, value in args.iteritems():
132    debug_annotation = proto.DebugAnnotation()
133    debug_annotation.name = name
134    if isinstance(value, int):
135      debug_annotation.int_value = value
136    elif isinstance(value, float):
137      debug_annotation.double_value = value
138    else:
139      debug_annotation.string_value = str(value)
140    packet.track_event.debug_annotations.append(debug_annotation)
141
142  proto.write_trace_packet(output, packet)
143
144
145def write_chrome_metadata(output, clock_domain):
146  """Write a chrome trace event with metadata.
147
148  Args:
149    output: a file-like object to write events into.
150    clock_domain: a string representing the trace clock domain.
151  """
152  chrome_metadata = proto.ChromeMetadata()
153  chrome_metadata.name = 'clock-domain'
154  chrome_metadata.string_value = clock_domain
155  chrome_event = proto.ChromeEventBundle()
156  chrome_event.metadata.append(chrome_metadata)
157  packet = proto.TracePacket()
158  packet.chrome_event = chrome_event
159  proto.write_trace_packet(output, packet)
160
161
162def write_metadata(
163    output,
164    benchmark_start_time_us,
165    story_run_time_us,
166    benchmark_name,
167    benchmark_description,
168    story_name,
169    story_tags,
170    story_run_index,
171    label=None,
172):
173  """Write a ChromeBenchmarkMetadata message."""
174  metadata = proto.ChromeBenchmarkMetadata()
175  metadata.benchmark_start_time_us = int(benchmark_start_time_us)
176  metadata.story_run_time_us = int(story_run_time_us)
177  metadata.benchmark_name = benchmark_name
178  metadata.benchmark_description = benchmark_description
179  metadata.story_name = story_name
180  metadata.story_tags = list(story_tags)
181  metadata.story_run_index = int(story_run_index)
182  if label is not None:
183    metadata.label = label
184
185  packet = proto.TracePacket()
186  packet.chrome_benchmark_metadata = metadata
187  proto.write_trace_packet(output, packet)
188
189
190def write_clock_snapshot(
191    output,
192    tid,
193    telemetry_ts=None,
194    boottime_ts=None,
195):
196  """Write a ClockSnapshot message.
197
198  Note that this function is NOT thread-safe.
199
200  Args:
201    output: a file-like object to write events into.
202    telemetry_ts: host BOOTTIME timestamp in microseconds.
203    boottime_ts: device BOOTTIME timestamp in microseconds.
204  """
205  clock_snapshot = proto.ClockSnapshot()
206  if telemetry_ts is not None:
207    clock = proto.Clock()
208    clock.clock_id = CLOCK_TELEMETRY
209    clock.timestamp = int(telemetry_ts * 1e3)
210    clock_snapshot.clocks.append(clock)
211  if boottime_ts is not None:
212    clock = proto.Clock()
213    clock.clock_id = CLOCK_BOOTTIME
214    clock.timestamp = int(boottime_ts * 1e3)
215    clock_snapshot.clocks.append(clock)
216  packet = proto.TracePacket()
217  packet.trusted_packet_sequence_id = _get_sequence_id(tid)
218  packet.clock_snapshot = clock_snapshot
219  proto.write_trace_packet(output, packet)
220