1#!/usr/bin/env python3
2# Copyright (C) 2018 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#      http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16import argparse
17
18from collections import namedtuple
19from google.protobuf import descriptor, descriptor_pb2, message_factory, descriptor_pool
20
21CLONE_THREAD = 0x00010000
22CLONE_VFORK = 0x00004000
23CLONE_VM = 0x00000100
24
25
26def ms_to_ns(time_in_ms):
27  return int(time_in_ms * 1000000)
28
29
30def s_to_ns(time_in_s):
31  return int(time_in_s * 1000000000)
32
33
34class Trace(object):
35
36  def __init__(self, trace, prototypes):
37    self.trace = trace
38    self.prototypes = prototypes
39    self.proc_map = {}
40    self.proc_map[0] = 'idle_thread'
41
42  def add_system_info(self, arch="", fingerprint=""):
43    self.packet = self.trace.packet.add()
44    self.packet.system_info.utsname.machine = arch
45    self.packet.system_info.android_build_fingerprint = fingerprint
46
47  def add_ftrace_packet(self, cpu):
48    self.packet = self.trace.packet.add()
49    self.packet.ftrace_events.cpu = cpu
50
51  def add_packet(self, ts=None):
52    self.packet = self.trace.packet.add()
53    if ts is not None:
54      self.packet.timestamp = ts
55    return self.packet
56
57  def __add_ftrace_event(self, ts, tid):
58    ftrace = self.packet.ftrace_events.event.add()
59    ftrace.timestamp = ts
60    ftrace.pid = tid
61    return ftrace
62
63  def add_rss_stat(self, ts, tid, member, size, mm_id=None, curr=None):
64    ftrace = self.__add_ftrace_event(ts, tid)
65    rss_stat = ftrace.rss_stat
66    rss_stat.member = member
67    rss_stat.size = size
68    if mm_id is not None:
69      rss_stat.mm_id = mm_id
70    if curr is not None:
71      rss_stat.curr = curr
72
73  def add_ion_event(self, ts, tid, heap_name, len, size=0):
74    ftrace = self.__add_ftrace_event(ts, tid)
75    ion = ftrace.ion_heap_grow
76    ion.heap_name = heap_name
77    ion.len = len
78    ion.total_allocated = size
79
80  def add_oom_score_update(self, ts, oom_score_adj, pid):
81    ftrace = self.__add_ftrace_event(ts, pid)
82    oom_score = ftrace.oom_score_adj_update
83    oom_score.comm = self.proc_map[pid]
84    oom_score.oom_score_adj = oom_score_adj
85    oom_score.pid = pid
86
87  def add_sched(self,
88                ts,
89                prev_pid,
90                next_pid,
91                prev_comm=None,
92                next_comm=None,
93                prev_state=None):
94    ftrace = self.__add_ftrace_event(ts, 0)
95    ss = ftrace.sched_switch
96    ss.prev_comm = prev_comm or self.proc_map[prev_pid]
97    ss.prev_pid = prev_pid
98    ss.next_pid = next_pid
99    ss.next_comm = next_comm or self.proc_map[next_pid]
100    if prev_state:
101      if prev_state == 'R':
102        ss.prev_state = 0
103      elif prev_state == 'S':
104        ss.prev_state = 1
105      elif prev_state == 'U':
106        ss.prev_state = 2
107      else:
108        raise Exception('Invalid prev state {}'.format(prev_state))
109
110  def add_cpufreq(self, ts, freq, cpu):
111    ftrace = self.__add_ftrace_event(ts, 0)
112    cpufreq = ftrace.cpu_frequency
113    cpufreq.state = freq
114    cpufreq.cpu_id = cpu
115
116  def add_kernel_lmk(self, ts, tid):
117    ftrace = self.__add_ftrace_event(ts, tid)
118    lowmemory_kill = ftrace.lowmemory_kill
119    lowmemory_kill.pid = tid
120
121  def add_sys_enter(self, ts, tid, id):
122    ftrace = self.__add_ftrace_event(ts, tid)
123    sys_enter = ftrace.sys_enter
124    sys_enter.id = id
125
126  def add_sys_exit(self, ts, tid, id, ret):
127    ftrace = self.__add_ftrace_event(ts, tid)
128    sys_exit = ftrace.sys_exit
129    sys_exit.id = id
130    sys_exit.ret = ret
131
132  def add_newtask(self, ts, tid, new_tid, new_comm, flags):
133    ftrace = self.__add_ftrace_event(ts, tid)
134    newtask = ftrace.task_newtask
135    newtask.pid = new_tid
136    newtask.comm = new_comm
137    newtask.clone_flags = flags
138
139  def add_process_free(self, ts, tid, comm, prio):
140    ftrace = self.__add_ftrace_event(ts, tid)
141    sched_process_free = ftrace.sched_process_free
142    sched_process_free.pid = tid
143    sched_process_free.comm = comm
144    sched_process_free.prio = prio
145
146  def add_rename(self, ts, tid, old_comm, new_comm, oom_score_adj):
147    ftrace = self.__add_ftrace_event(ts, tid)
148    task_rename = ftrace.task_rename
149    task_rename.pid = tid
150    task_rename.oldcomm = old_comm
151    task_rename.newcomm = new_comm
152    task_rename.oom_score_adj = oom_score_adj
153
154  def add_print(self, ts, tid, buf):
155    ftrace = self.__add_ftrace_event(ts, tid)
156    print_event = getattr(ftrace, 'print')
157    print_event.buf = buf
158
159  def add_kmalloc(self, ts, tid, bytes_alloc, bytes_req, call_site, gfp_flags,
160                  ptr):
161    ftrace = self.__add_ftrace_event(ts, tid)
162    kmalloc = ftrace.kmalloc
163    kmalloc.bytes_alloc = bytes_alloc
164    kmalloc.bytes_req = bytes_req
165    kmalloc.call_site = call_site
166    kmalloc.gfp_flags = gfp_flags
167    kmalloc.ptr = ptr
168
169  def add_kfree(self, ts, tid, call_site, ptr):
170    ftrace = self.__add_ftrace_event(ts, tid)
171    kfree = ftrace.kfree
172    kfree.call_site = call_site
173    kfree.ptr = ptr
174
175  def add_atrace_counter(self, ts, pid, tid, buf, cnt):
176    self.add_print(ts, tid, 'C|{}|{}|{}'.format(pid, buf, cnt))
177
178  def add_atrace_begin(self, ts, tid, pid, buf):
179    self.add_print(ts, tid, 'B|{}|{}'.format(pid, buf))
180
181  def add_atrace_end(self, ts, tid, pid):
182    self.add_print(ts, tid, 'E|{}'.format(pid))
183
184  def add_atrace_async_begin(self, ts, tid, pid, buf):
185    self.add_print(ts, tid, 'S|{}|{}|0'.format(pid, buf))
186
187  def add_atrace_async_end(self, ts, tid, pid, buf):
188    self.add_print(ts, tid, 'F|{}|{}|0'.format(pid, buf))
189
190  def add_process(self, pid, ppid, cmdline, uid=None):
191    process = self.packet.process_tree.processes.add()
192    process.pid = pid
193    process.ppid = ppid
194    process.cmdline.append(cmdline)
195    if uid is not None:
196      process.uid = uid
197    self.proc_map[pid] = cmdline
198
199  def add_thread(self, tid, tgid, cmdline, name=None):
200    thread = self.packet.process_tree.threads.add()
201    thread.tid = tid
202    thread.tgid = tgid
203    if name is not None:
204      thread.name = name
205    self.proc_map[tid] = cmdline
206
207  def add_battery_counters(self, ts, charge_uah, cap_prct, curr_ua,
208                           curr_avg_ua):
209    self.packet = self.trace.packet.add()
210    self.packet.timestamp = ts
211    battery_count = self.packet.battery
212    battery_count.charge_counter_uah = charge_uah
213    battery_count.capacity_percent = cap_prct
214    battery_count.current_ua = curr_ua
215    battery_count.current_avg_ua = curr_avg_ua
216
217  def add_battery_counters_no_curr_ua(self, ts, charge_uah, cap_prct,
218                                      curr_avg_ua):
219    self.packet = self.trace.packet.add()
220    self.packet.timestamp = ts
221    battery_count = self.packet.battery
222    battery_count.charge_counter_uah = charge_uah
223    battery_count.capacity_percent = cap_prct
224    battery_count.current_avg_ua = curr_avg_ua
225
226  def add_power_rails_desc(self, index_val, name):
227    power_rails = self.packet.power_rails
228    descriptor = power_rails.rail_descriptor.add()
229    descriptor.index = index_val
230    descriptor.rail_name = name
231
232  def add_power_rails_data(self, ts, index_val, value):
233    power_rails = self.packet.power_rails
234    energy_data = power_rails.energy_data.add()
235    energy_data.index = index_val
236    energy_data.timestamp_ms = ts
237    energy_data.energy = value
238
239  def add_package_list(self, ts, name, uid, version_code):
240    packet = self.add_packet()
241    packet.timestamp = ts
242    plist = packet.packages_list
243    pinfo = plist.packages.add()
244    pinfo.name = name
245    pinfo.uid = uid
246    pinfo.version_code = version_code
247
248  def add_profile_packet(self, ts):
249    packet = self.add_packet()
250    packet.timestamp = ts
251    return packet.profile_packet
252
253  def add_clock_snapshot(self, clocks, seq_id=None):
254    packet = self.add_packet()
255    if seq_id is not None:
256      packet.trusted_packet_sequence_id = seq_id
257    snap = self.packet.clock_snapshot
258    for k, v in clocks.items():
259      clock = snap.clocks.add()
260      clock.clock_id = k
261      clock.timestamp = v
262
263  def add_gpu_counter_spec(self,
264                           ts,
265                           counter_id,
266                           name,
267                           description=None,
268                           unit_numerators=[],
269                           unit_denominators=[]):
270    packet = self.add_packet()
271    packet.timestamp = ts
272    gpu_counters = packet.gpu_counter_event
273    counter_desc = gpu_counters.counter_descriptor
274    spec = counter_desc.specs.add()
275    spec.counter_id = counter_id
276    spec.name = name
277    if description is not None:
278      spec.description = description
279    spec.numerator_units.extend(unit_numerators)
280    spec.denominator_units.extend(unit_denominators)
281
282  def add_gpu_counter(self, ts, counter_id, value, clock_id=None, seq_id=None):
283    packet = self.add_packet()
284    packet.timestamp = ts
285    if clock_id is not None:
286      packet.timestamp_clock_id = clock_id
287    if seq_id is not None:
288      packet.trusted_packet_sequence_id = seq_id
289    gpu_counters = self.packet.gpu_counter_event
290    gpu_counter = gpu_counters.counters.add()
291    gpu_counter.counter_id = counter_id
292    gpu_counter.int_value = value
293
294  def add_gpu_render_stages_hw_queue_spec(self, specs=[]):
295    packet = self.add_packet()
296    spec = self.packet.gpu_render_stage_event.specifications
297    for s in specs:
298      hw_queue = spec.hw_queue.add()
299      hw_queue.name = s.get('name', '')
300      if 'description' in s:
301        hw_queue.description = s['description']
302
303  def add_gpu_render_stages_stage_spec(self, specs=[]):
304    packet = self.add_packet()
305    spec = self.packet.gpu_render_stage_event.specifications
306    for s in specs:
307      stage = spec.stage.add()
308      stage.name = s.get('name', '')
309      if 'description' in s:
310        stage.description = s['description']
311
312  def add_gpu_render_stages(self,
313                            ts,
314                            event_id,
315                            duration,
316                            hw_queue_id,
317                            stage_id,
318                            context,
319                            render_target_handle=None,
320                            render_pass_handle=None,
321                            render_subpass_index_mask=None,
322                            command_buffer_handle=None,
323                            submission_id=None,
324                            extra_data={}):
325    packet = self.add_packet()
326    packet.timestamp = ts
327    render_stage = self.packet.gpu_render_stage_event
328    render_stage.event_id = event_id
329    render_stage.duration = duration
330    render_stage.hw_queue_id = hw_queue_id
331    render_stage.stage_id = stage_id
332    render_stage.context = context
333    if render_target_handle is not None:
334      render_stage.render_target_handle = render_target_handle
335    if render_pass_handle is not None:
336      render_stage.render_pass_handle = render_pass_handle
337    if render_subpass_index_mask is not None:
338      for mask in render_subpass_index_mask:
339        render_stage.render_subpass_index_mask.append(mask)
340    if command_buffer_handle is not None:
341      render_stage.command_buffer_handle = command_buffer_handle
342    if submission_id is not None:
343      render_stage.submission_id = submission_id
344    for key, value in extra_data.items():
345      data = render_stage.extra_data.add()
346      data.name = key
347      if value is not None:
348        data.value = value
349
350  def add_vk_debug_marker(self, ts, pid, vk_device, obj_type, obj, obj_name):
351    packet = self.add_packet()
352    packet.timestamp = ts
353    debug_marker = (self.packet.vulkan_api_event.vk_debug_utils_object_name)
354    debug_marker.pid = pid
355    debug_marker.vk_device = vk_device
356    debug_marker.object_type = obj_type
357    debug_marker.object = obj
358    debug_marker.object_name = obj_name
359
360  def add_vk_queue_submit(self, ts, dur, pid, tid, vk_queue, vk_command_buffers,
361                          submission_id):
362    packet = self.add_packet()
363    packet.timestamp = ts
364    submit = (self.packet.vulkan_api_event.vk_queue_submit)
365    submit.duration_ns = dur
366    submit.pid = pid
367    submit.tid = tid
368    for cmd in vk_command_buffers:
369      submit.vk_command_buffers.append(cmd)
370    submit.submission_id = submission_id
371
372  def add_gpu_log(self, ts, severity, tag, message):
373    packet = self.add_packet()
374    packet.timestamp = ts
375    gpu_log = self.packet.gpu_log
376    gpu_log.severity = severity
377    gpu_log.tag = tag
378    gpu_log.log_message = message
379
380  def add_buffer_event_packet(self, ts, buffer_id, layer_name, frame_number,
381                              event_type, duration):
382    packet = self.add_packet()
383    packet.timestamp = ts
384    buffer_event = packet.graphics_frame_event.buffer_event
385    if buffer_id >= 0:
386      buffer_event.buffer_id = buffer_id
387    buffer_event.layer_name = layer_name
388    buffer_event.frame_number = frame_number
389    if event_type >= 0:
390      buffer_event.type = event_type
391    buffer_event.duration_ns = duration
392
393  def add_cpu(self, freqs):
394    cpu = self.packet.cpu_info.cpus.add()
395    for freq in freqs:
396      cpu.frequencies.append(freq)
397
398  def add_process_stats(self, pid, freqs):
399    process = self.packet.process_stats.processes.add()
400    process.pid = pid
401    thread = process.threads.add()
402    thread.tid = pid * 10
403    for index in freqs:
404      thread.cpu_freq_indices.append(index)
405      thread.cpu_freq_ticks.append(freqs[index])
406
407  def add_gpu_mem_total_ftrace_event(self, pid, ts, size):
408    ftrace = self.__add_ftrace_event(ts, pid)
409    gpu_mem_total_ftrace_event = ftrace.gpu_mem_total
410    gpu_mem_total_ftrace_event.pid = pid
411    gpu_mem_total_ftrace_event.size = size
412
413  def add_gpu_mem_total_event(self, pid, ts, size):
414    packet = self.add_packet()
415    packet.timestamp = ts
416    gpu_mem_total_event = packet.gpu_mem_total_event
417    gpu_mem_total_event.pid = pid
418    gpu_mem_total_event.size = size
419
420  def add_sched_blocked_reason(self, ts, pid, io_wait, unblock_pid):
421    ftrace = self.__add_ftrace_event(ts, unblock_pid)
422    sched_blocked_reason = ftrace.sched_blocked_reason
423    sched_blocked_reason.pid = pid
424    sched_blocked_reason.io_wait = io_wait
425
426  def add_track_event(self,
427                      name=None,
428                      ts=None,
429                      track=None,
430                      trusted_sequence_id=0,
431                      cpu_time=None):
432    packet = self.add_packet(ts=ts)
433    if name is not None:
434      packet.track_event.name = name
435    if track is not None:
436      packet.track_event.track_uuid = track
437    packet.trusted_packet_sequence_id = trusted_sequence_id
438    if cpu_time is not None:
439      packet.track_event.extra_counter_values.append(cpu_time)
440    return packet
441
442  def add_track_descriptor(self, uuid, parent=None):
443    packet = self.add_packet()
444    track_descriptor = packet.track_descriptor
445    if uuid is not None:
446      track_descriptor.uuid = uuid
447    if parent is not None:
448      track_descriptor.parent_uuid = parent
449    return packet
450
451  def add_process_track_descriptor(self, process_track, pid=None):
452    packet = self.add_track_descriptor(process_track)
453    packet.track_descriptor.process.pid = pid
454    return packet
455
456  def add_chrome_process_track_descriptor(
457      self,
458      process_track,
459      pid=None,
460      process_type=None,
461      host_app_package_name="org.chromium.chrome"):
462    if process_type is None:
463      process_type = self.prototypes.ChromeProcessDescriptor \
464        .ProcessType \
465        .PROCESS_RENDERER
466
467    packet = self.add_process_track_descriptor(process_track, pid=pid)
468    chrome_process = packet.track_descriptor.chrome_process
469    chrome_process.process_type = process_type
470    chrome_process.host_app_package_name = host_app_package_name
471    return packet
472
473  def add_thread_track_descriptor(self,
474                                  process_track,
475                                  thread_track,
476                                  trusted_packet_sequence_id=None,
477                                  pid=None,
478                                  tid=None):
479    packet = self.add_track_descriptor(thread_track, parent=process_track)
480    packet.trusted_packet_sequence_id = trusted_packet_sequence_id
481    packet.track_descriptor.thread.pid = pid
482    packet.track_descriptor.thread.tid = tid
483    return packet
484
485  def add_chrome_thread_track_descriptor(self,
486                                         process_track,
487                                         thread_track,
488                                         trusted_packet_sequence_id=None,
489                                         pid=None,
490                                         tid=None,
491                                         thread_type=None):
492    if thread_type is None:
493      thread_type = self.prototypes.ThreadDescriptor \
494        .ChromeThreadType \
495        .CHROME_THREAD_TYPE_UNSPECIFIED
496
497    packet = self.add_thread_track_descriptor(
498        process_track,
499        thread_track,
500        trusted_packet_sequence_id=trusted_packet_sequence_id,
501        pid=pid,
502        tid=tid)
503    packet.track_descriptor.thread.chrome_thread_type = thread_type
504    return packet
505
506  def add_trace_packet_defaults(self,
507                                trusted_packet_sequence_id=None,
508                                thread_track=None,
509                                counter_track=None):
510    packet = self.add_track_descriptor(None)
511    packet.trusted_packet_sequence_id = trusted_packet_sequence_id
512    track_event_defaults = packet.trace_packet_defaults.track_event_defaults
513    track_event_defaults.track_uuid = thread_track
514    track_event_defaults.extra_counter_track_uuids.append(counter_track)
515    return packet
516
517  def add_counter_track_descriptor(self,
518                                   trusted_packet_sequence_id=None,
519                                   thread_track=None,
520                                   counter_track=None):
521    packet = self.add_track_descriptor(counter_track, parent=thread_track)
522    packet.trusted_packet_sequence_id = trusted_packet_sequence_id
523    packet.track_descriptor.counter.type = self.prototypes.CounterDescriptor \
524      .BuiltinCounterType \
525      .COUNTER_THREAD_TIME_NS
526    return packet
527
528  def add_chrome_thread_with_cpu_counter(self,
529                                         process_track,
530                                         thread_track,
531                                         trusted_packet_sequence_id=None,
532                                         counter_track=None,
533                                         pid=None,
534                                         tid=None,
535                                         thread_type=None):
536    self.add_chrome_thread_track_descriptor(
537        process_track,
538        thread_track,
539        trusted_packet_sequence_id=trusted_packet_sequence_id,
540        pid=pid,
541        tid=tid,
542        thread_type=thread_type)
543    self.add_trace_packet_defaults(
544        trusted_packet_sequence_id=trusted_packet_sequence_id,
545        counter_track=counter_track,
546        thread_track=thread_track)
547
548    self.add_counter_track_descriptor(
549        trusted_packet_sequence_id=trusted_packet_sequence_id,
550        counter_track=counter_track,
551        thread_track=thread_track)
552
553  def add_track_event_slice_begin(self,
554                                  name,
555                                  ts,
556                                  track=None,
557                                  trusted_sequence_id=0,
558                                  cpu_time=None):
559    packet = self.add_track_event(
560        name,
561        ts=ts,
562        track=track,
563        trusted_sequence_id=trusted_sequence_id,
564        cpu_time=cpu_time)
565    packet.track_event.type = self.prototypes.TrackEvent.Type.TYPE_SLICE_BEGIN
566    return packet
567
568  def add_track_event_slice_end(self,
569                                ts,
570                                track=None,
571                                trusted_sequence_id=0,
572                                cpu_time=None):
573    packet = self.add_track_event(
574        ts=ts,
575        track=track,
576        trusted_sequence_id=trusted_sequence_id,
577        cpu_time=cpu_time)
578    packet.track_event.type = self.prototypes.TrackEvent.Type.TYPE_SLICE_END
579    return packet
580
581  # Returns the start slice packet.
582  def add_track_event_slice(self,
583                            name,
584                            ts,
585                            dur,
586                            track=None,
587                            trusted_sequence_id=0,
588                            cpu_start=None,
589                            cpu_delta=None):
590
591    packet = self.add_track_event_slice_begin(
592        name,
593        ts,
594        track=track,
595        trusted_sequence_id=trusted_sequence_id,
596        cpu_time=cpu_start)
597
598    if dur >= 0:
599      cpu_end = cpu_start + cpu_delta if cpu_start is not None else None
600      self.add_track_event_slice_end(
601          ts + dur,
602          track=track,
603          trusted_sequence_id=trusted_sequence_id,
604          cpu_time=cpu_end)
605
606    return packet
607
608  def add_rail_mode_slice(self, ts, dur, track, mode):
609    packet = self.add_track_event_slice(
610        "Scheduler.RAILMode", ts=ts, dur=dur, track=track)
611    packet.track_event.chrome_renderer_scheduler_state.rail_mode = mode
612
613  def add_latency_info_flow(self,
614                            ts,
615                            dur,
616                            track=None,
617                            trusted_sequence_id=None,
618                            trace_id=None,
619                            flow_ids=[],
620                            terminating_flow_ids=[]):
621    packet = self.add_track_event_slice(
622        "LatencyInfo.Flow",
623        ts,
624        dur,
625        track=track,
626        trusted_sequence_id=trusted_sequence_id)
627    if trace_id is not None:
628      packet.track_event.chrome_latency_info.trace_id = trace_id
629    for flow_id in flow_ids:
630      packet.track_event.flow_ids.append(flow_id)
631    for flow_id in terminating_flow_ids:
632      packet.track_event.terminating_flow_ids.append(flow_id)
633    return packet
634
635  def add_input_latency_event_slice(self,
636                                    name,
637                                    ts,
638                                    dur,
639                                    track=None,
640                                    trace_id=None,
641                                    gesture_scroll_id=None,
642                                    is_coalesced=None):
643    packet = self.add_track_event_slice(
644        "InputLatency::" + name, ts=ts, dur=dur, track=track)
645    packet.track_event.chrome_latency_info.trace_id = trace_id
646    packet.track_event.chrome_latency_info.gesture_scroll_id = gesture_scroll_id
647    if is_coalesced is not None:
648      packet.track_event.chrome_latency_info.is_coalesced = is_coalesced
649    return packet
650
651  def add_chrome_metadata(self, os_name=None):
652    metadata = self.add_packet().chrome_events.metadata.add()
653    if os_name is not None:
654      metadata.name = "os-name"
655      metadata.string_value = os_name
656
657    return metadata
658
659  def add_expected_display_frame_start_event(self, ts, cookie, token, pid):
660    packet = self.add_packet()
661    packet.timestamp = ts
662    event = packet.frame_timeline_event.expected_display_frame_start
663    if token != -1:
664      event.cookie = cookie
665      event.token = token
666      event.pid = pid
667
668  def add_actual_display_frame_start_event(self, ts, cookie, token, pid,
669                                           present_type, on_time_finish,
670                                           gpu_composition, jank_type, prediction_type):
671    packet = self.add_packet()
672    packet.timestamp = ts
673    event = packet.frame_timeline_event.actual_display_frame_start
674    if token != -1:
675      event.cookie = cookie
676      event.token = token
677      event.pid = pid
678      event.present_type = present_type
679      event.on_time_finish = on_time_finish
680      event.gpu_composition = gpu_composition
681      event.jank_type = jank_type
682      event.prediction_type = prediction_type
683
684  def add_expected_surface_frame_start_event(self, ts, cookie, token,
685                                             display_frame_token, pid,
686                                             layer_name):
687    packet = self.add_packet()
688    packet.timestamp = ts
689    event = packet.frame_timeline_event.expected_surface_frame_start
690    if token != -1 and display_frame_token != -1:
691      event.cookie = cookie
692      event.token = token
693      event.display_frame_token = display_frame_token
694      event.pid = pid
695      event.layer_name = layer_name
696
697  def add_actual_surface_frame_start_event(self, ts, cookie, token,
698                                           display_frame_token, pid, layer_name,
699                                           present_type, on_time_finish,
700                                           gpu_composition, jank_type, prediction_type):
701    packet = self.add_packet()
702    packet.timestamp = ts
703    event = packet.frame_timeline_event.actual_surface_frame_start
704    if token != -1 and display_frame_token != -1:
705      event.cookie = cookie
706      event.token = token
707      event.display_frame_token = display_frame_token
708      event.pid = pid
709      event.layer_name = layer_name
710      event.present_type = present_type
711      event.on_time_finish = on_time_finish
712      event.gpu_composition = gpu_composition
713      event.jank_type = jank_type
714      event.prediction_type = prediction_type
715
716  def add_frame_end_event(self, ts, cookie):
717    packet = self.add_packet()
718    packet.timestamp = ts
719    event = packet.frame_timeline_event.frame_end
720    event.cookie = cookie
721
722
723def read_descriptor(file_name):
724  with open(file_name, 'rb') as f:
725    contents = f.read()
726
727  descriptor = descriptor_pb2.FileDescriptorSet()
728  descriptor.MergeFromString(contents)
729
730  return descriptor
731
732
733def create_pool(args):
734  trace_descriptor = read_descriptor(args.trace_descriptor)
735
736  pool = descriptor_pool.DescriptorPool()
737  for file in trace_descriptor.file:
738    pool.Add(file)
739
740  return pool
741
742
743def create_trace():
744  parser = argparse.ArgumentParser()
745  parser.add_argument(
746      'trace_descriptor', type=str, help='location of trace descriptor')
747  args = parser.parse_args()
748
749  pool = create_pool(args)
750  factory = message_factory.MessageFactory(pool)
751  ProtoTrace = factory.GetPrototype(
752      pool.FindMessageTypeByName('perfetto.protos.Trace'))
753
754  class EnumPrototype(object):
755
756    def from_descriptor(desc):
757      res = EnumPrototype()
758      for desc in desc.values:
759        setattr(res, desc.name, desc.number)
760      return res
761
762  Prototypes = namedtuple('Prototypes', [
763      'TrackEvent',
764      'ChromeRAILMode',
765      'ThreadDescriptor',
766      'ChromeProcessDescriptor',
767      'CounterDescriptor',
768  ])
769  prototypes = Prototypes(
770      TrackEvent=factory.GetPrototype(
771          pool.FindMessageTypeByName('perfetto.protos.TrackEvent')),
772      ChromeRAILMode=EnumPrototype.from_descriptor(
773          pool.FindEnumTypeByName('perfetto.protos.ChromeRAILMode')),
774      ThreadDescriptor=factory.GetPrototype(
775          pool.FindMessageTypeByName('perfetto.protos.ThreadDescriptor')),
776      ChromeProcessDescriptor=factory.GetPrototype(
777          pool.FindMessageTypeByName(
778              'perfetto.protos.ChromeProcessDescriptor')),
779      CounterDescriptor=factory.GetPrototype(
780          pool.FindMessageTypeByName('perfetto.protos.CounterDescriptor')),
781  )
782  return Trace(ProtoTrace(), prototypes)
783