1 /*
2 * Copyright (C) 2020 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 */
16
17 #include "src/trace_processor/importers/proto/profile_module.h"
18
19 #include "perfetto/base/logging.h"
20 #include "src/trace_processor/importers/common/clock_tracker.h"
21 #include "src/trace_processor/importers/common/process_tracker.h"
22 #include "src/trace_processor/importers/proto/packet_sequence_state.h"
23 #include "src/trace_processor/importers/proto/profile_packet_utils.h"
24 #include "src/trace_processor/importers/proto/stack_profile_tracker.h"
25 #include "src/trace_processor/storage/trace_storage.h"
26 #include "src/trace_processor/tables/profiler_tables.h"
27 #include "src/trace_processor/timestamped_trace_piece.h"
28 #include "src/trace_processor/trace_sorter.h"
29 #include "src/trace_processor/types/trace_processor_context.h"
30
31 #include "protos/perfetto/common/builtin_clock.pbzero.h"
32 #include "protos/perfetto/trace/profiling/profile_packet.pbzero.h"
33
34 namespace perfetto {
35 namespace trace_processor {
36
37 using perfetto::protos::pbzero::TracePacket;
38 using protozero::ConstBytes;
39
ProfileModule(TraceProcessorContext * context)40 ProfileModule::ProfileModule(TraceProcessorContext* context)
41 : context_(context) {
42 RegisterForField(TracePacket::kStreamingProfilePacketFieldNumber, context);
43 }
44
45 ProfileModule::~ProfileModule() = default;
46
TokenizePacket(const TracePacket::Decoder & decoder,TraceBlobView * packet,int64_t,PacketSequenceState * state,uint32_t field_id)47 ModuleResult ProfileModule::TokenizePacket(const TracePacket::Decoder& decoder,
48 TraceBlobView* packet,
49 int64_t /*packet_timestamp*/,
50 PacketSequenceState* state,
51 uint32_t field_id) {
52 switch (field_id) {
53 case TracePacket::kStreamingProfilePacketFieldNumber:
54 return TokenizeStreamingProfilePacket(state, packet,
55 decoder.streaming_profile_packet());
56 }
57 return ModuleResult::Ignored();
58 }
59
ParsePacket(const TracePacket::Decoder & decoder,const TimestampedTracePiece & ttp,uint32_t field_id)60 void ProfileModule::ParsePacket(const TracePacket::Decoder& decoder,
61 const TimestampedTracePiece& ttp,
62 uint32_t field_id) {
63 switch (field_id) {
64 case TracePacket::kStreamingProfilePacketFieldNumber:
65 PERFETTO_DCHECK(ttp.type == TimestampedTracePiece::Type::kTracePacket);
66 ParseStreamingProfilePacket(ttp.timestamp, ttp.packet_data.sequence_state,
67 decoder.streaming_profile_packet());
68 return;
69 }
70 }
71
TokenizeStreamingProfilePacket(PacketSequenceState * sequence_state,TraceBlobView * packet,ConstBytes streaming_profile_packet)72 ModuleResult ProfileModule::TokenizeStreamingProfilePacket(
73 PacketSequenceState* sequence_state,
74 TraceBlobView* packet,
75 ConstBytes streaming_profile_packet) {
76 protos::pbzero::StreamingProfilePacket::Decoder decoder(
77 streaming_profile_packet.data, streaming_profile_packet.size);
78
79 // We have to resolve the reference timestamp of a StreamingProfilePacket
80 // during tokenization. If we did this during parsing instead, the
81 // tokenization of a subsequent ThreadDescriptor with a new reference
82 // timestamp would cause us to later calculate timestamps based on the wrong
83 // reference value during parsing. Since StreamingProfilePackets only need to
84 // be sorted correctly with respect to process/thread metadata events (so that
85 // pid/tid are resolved correctly during parsing), we forward the packet as a
86 // whole through the sorter, using the "root" timestamp of the packet, i.e.
87 // the current timestamp of the packet sequence.
88 auto packet_ts =
89 sequence_state->IncrementAndGetTrackEventTimeNs(/*delta_ns=*/0);
90 auto trace_ts = context_->clock_tracker->ToTraceTime(
91 protos::pbzero::BUILTIN_CLOCK_MONOTONIC, packet_ts);
92 if (trace_ts)
93 packet_ts = *trace_ts;
94
95 // Increment the sequence's timestamp by all deltas.
96 for (auto timestamp_it = decoder.timestamp_delta_us(); timestamp_it;
97 ++timestamp_it) {
98 sequence_state->IncrementAndGetTrackEventTimeNs(*timestamp_it * 1000);
99 }
100
101 context_->sorter->PushTracePacket(packet_ts, sequence_state,
102 std::move(*packet));
103 return ModuleResult::Handled();
104 }
105
ParseStreamingProfilePacket(int64_t timestamp,PacketSequenceStateGeneration * sequence_state,ConstBytes streaming_profile_packet)106 void ProfileModule::ParseStreamingProfilePacket(
107 int64_t timestamp,
108 PacketSequenceStateGeneration* sequence_state,
109 ConstBytes streaming_profile_packet) {
110 protos::pbzero::StreamingProfilePacket::Decoder packet(
111 streaming_profile_packet.data, streaming_profile_packet.size);
112
113 ProcessTracker* procs = context_->process_tracker.get();
114 TraceStorage* storage = context_->storage.get();
115 StackProfileTracker& stack_profile_tracker =
116 sequence_state->state()->stack_profile_tracker();
117 ProfilePacketInternLookup intern_lookup(sequence_state);
118
119 uint32_t pid = static_cast<uint32_t>(sequence_state->state()->pid());
120 uint32_t tid = static_cast<uint32_t>(sequence_state->state()->tid());
121 UniqueTid utid = procs->UpdateThread(tid, pid);
122
123 // Iterate through timestamps and callstacks simultaneously.
124 auto timestamp_it = packet.timestamp_delta_us();
125 for (auto callstack_it = packet.callstack_iid(); callstack_it;
126 ++callstack_it, ++timestamp_it) {
127 if (!timestamp_it) {
128 context_->storage->IncrementStats(stats::stackprofile_parser_error);
129 PERFETTO_ELOG(
130 "StreamingProfilePacket has less callstack IDs than timestamps!");
131 break;
132 }
133
134 auto opt_cs_id = stack_profile_tracker.FindOrInsertCallstack(
135 *callstack_it, &intern_lookup);
136 if (!opt_cs_id) {
137 context_->storage->IncrementStats(stats::stackprofile_parser_error);
138 PERFETTO_ELOG("StreamingProfilePacket referencing invalid callstack!");
139 continue;
140 }
141
142 // Resolve the delta timestamps based on the packet's root timestamp.
143 timestamp += *timestamp_it * 1000;
144
145 tables::CpuProfileStackSampleTable::Row sample_row{
146 timestamp, *opt_cs_id, utid, packet.process_priority()};
147 storage->mutable_cpu_profile_stack_sample_table()->Insert(sample_row);
148 }
149 }
150
151 } // namespace trace_processor
152 } // namespace perfetto
153