1 /*
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  */
16 
17 #include <limits>
18 
19 #include <stdint.h>
20 
21 #include "src/trace_processor/process_tracker.h"
22 #include "src/trace_processor/slice_tracker.h"
23 #include "src/trace_processor/trace_processor_context.h"
24 #include "src/trace_processor/trace_storage.h"
25 
26 namespace perfetto {
27 namespace trace_processor {
28 namespace {
29 // Slices which have been opened but haven't been closed yet will be marked
30 // with this duration placeholder.
31 constexpr int64_t kPendingDuration = -1;
32 };  // namespace
33 
SliceTracker(TraceProcessorContext * context)34 SliceTracker::SliceTracker(TraceProcessorContext* context)
35     : context_(context) {}
36 
37 SliceTracker::~SliceTracker() = default;
38 
BeginAndroid(int64_t timestamp,uint32_t ftrace_tid,uint32_t atrace_tgid,StringId cat,StringId name)39 void SliceTracker::BeginAndroid(int64_t timestamp,
40                                 uint32_t ftrace_tid,
41                                 uint32_t atrace_tgid,
42                                 StringId cat,
43                                 StringId name) {
44   UniqueTid utid =
45       context_->process_tracker->UpdateThread(ftrace_tid, atrace_tgid);
46   ftrace_to_atrace_tgid_[ftrace_tid] = atrace_tgid;
47   Begin(timestamp, utid, cat, name);
48 }
49 
Begin(int64_t timestamp,UniqueTid utid,StringId cat,StringId name)50 void SliceTracker::Begin(int64_t timestamp,
51                          UniqueTid utid,
52                          StringId cat,
53                          StringId name) {
54   MaybeCloseStack(timestamp, &threads_[utid]);
55   StartSlice(timestamp, kPendingDuration, utid, cat, name);
56 }
57 
Scoped(int64_t timestamp,UniqueTid utid,StringId cat,StringId name,int64_t duration)58 void SliceTracker::Scoped(int64_t timestamp,
59                           UniqueTid utid,
60                           StringId cat,
61                           StringId name,
62                           int64_t duration) {
63   PERFETTO_DCHECK(duration >= 0);
64   MaybeCloseStack(timestamp, &threads_[utid]);
65   StartSlice(timestamp, duration, utid, cat, name);
66 }
67 
StartSlice(int64_t timestamp,int64_t duration,UniqueTid utid,StringId cat,StringId name)68 void SliceTracker::StartSlice(int64_t timestamp,
69                               int64_t duration,
70                               UniqueTid utid,
71                               StringId cat,
72                               StringId name) {
73   auto* stack = &threads_[utid];
74   auto* slices = context_->storage->mutable_nestable_slices();
75 
76   const uint8_t depth = static_cast<uint8_t>(stack->size());
77   if (depth >= std::numeric_limits<uint8_t>::max()) {
78     PERFETTO_DFATAL("Slices with too large depth found.");
79     return;
80   }
81   int64_t parent_stack_id = depth == 0 ? 0 : slices->stack_ids()[stack->back()];
82   size_t slice_idx =
83       slices->AddSlice(timestamp, duration, utid, RefType::kRefUtid, cat, name,
84                        depth, 0, parent_stack_id);
85   stack->emplace_back(slice_idx);
86 
87   slices->set_stack_id(slice_idx, GetStackHash(*stack));
88 }
89 
EndAndroid(int64_t timestamp,uint32_t ftrace_tid,uint32_t atrace_tgid)90 void SliceTracker::EndAndroid(int64_t timestamp,
91                               uint32_t ftrace_tid,
92                               uint32_t atrace_tgid) {
93   auto actual_tgid_it = ftrace_to_atrace_tgid_.find(ftrace_tid);
94   if (actual_tgid_it == ftrace_to_atrace_tgid_.end()) {
95     // This is possible if we start tracing after a begin slice.
96     PERFETTO_DLOG("Unknown tgid for ftrace tid %u", ftrace_tid);
97     return;
98   }
99   uint32_t actual_tgid = actual_tgid_it->second;
100   // atrace_tgid can be 0 in older android versions where the end event would
101   // not contain the value.
102   if (atrace_tgid != 0 && atrace_tgid != actual_tgid) {
103     PERFETTO_DLOG("Mismatched atrace pid %u and looked up pid %u", atrace_tgid,
104                   actual_tgid);
105     context_->storage->IncrementStats(stats::atrace_tgid_mismatch);
106   }
107   UniqueTid utid =
108       context_->process_tracker->UpdateThread(ftrace_tid, actual_tgid);
109   End(timestamp, utid);
110 }
111 
End(int64_t timestamp,UniqueTid utid,StringId cat,StringId name)112 void SliceTracker::End(int64_t timestamp,
113                        UniqueTid utid,
114                        StringId cat,
115                        StringId name) {
116   MaybeCloseStack(timestamp, &threads_[utid]);
117 
118   const auto& stack = threads_[utid];
119   if (stack.empty())
120     return;
121 
122   auto* slices = context_->storage->mutable_nestable_slices();
123   size_t slice_idx = stack.back();
124 
125   // If we are trying to close mismatching slices (e.g., slices that began
126   // before tracing started), bail out.
127   if (cat && slices->cats()[slice_idx] != cat)
128     return;
129   if (name && slices->names()[slice_idx] != name)
130     return;
131 
132   PERFETTO_DCHECK(slices->durations()[slice_idx] == kPendingDuration);
133   slices->set_duration(slice_idx, timestamp - slices->start_ns()[slice_idx]);
134 
135   CompleteSlice(utid);
136   // TODO(primiano): auto-close B slices left open at the end.
137 }
138 
CompleteSlice(UniqueTid utid)139 void SliceTracker::CompleteSlice(UniqueTid utid) {
140   threads_[utid].pop_back();
141 }
142 
MaybeCloseStack(int64_t ts,SlicesStack * stack)143 void SliceTracker::MaybeCloseStack(int64_t ts, SlicesStack* stack) {
144   const auto& slices = context_->storage->nestable_slices();
145   bool check_only = false;
146   for (int i = static_cast<int>(stack->size()) - 1; i >= 0; i--) {
147     size_t slice_idx = (*stack)[static_cast<size_t>(i)];
148 
149     int64_t start_ts = slices.start_ns()[slice_idx];
150     int64_t dur = slices.durations()[slice_idx];
151     int64_t end_ts = start_ts + dur;
152     if (dur == kPendingDuration) {
153       check_only = true;
154     }
155 
156     if (check_only) {
157       PERFETTO_CHECK(ts >= start_ts);
158       PERFETTO_CHECK(dur == kPendingDuration || ts <= end_ts);
159       continue;
160     }
161 
162     if (end_ts <= ts) {
163       stack->pop_back();
164     }
165   }
166 }
167 
GetStackHash(const SlicesStack & stack)168 int64_t SliceTracker::GetStackHash(const SlicesStack& stack) {
169   PERFETTO_DCHECK(!stack.empty());
170 
171   const auto& slices = context_->storage->nestable_slices();
172 
173   std::string s;
174   s.reserve(stack.size() * sizeof(uint64_t) * 2);
175   for (size_t i = 0; i < stack.size(); i++) {
176     size_t slice_idx = stack[i];
177     s.append(reinterpret_cast<const char*>(&slices.cats()[slice_idx]),
178              sizeof(slices.cats()[slice_idx]));
179     s.append(reinterpret_cast<const char*>(&slices.names()[slice_idx]),
180              sizeof(slices.names()[slice_idx]));
181   }
182   constexpr uint64_t kMask = uint64_t(-1) >> 1;
183   return static_cast<int64_t>((std::hash<std::string>{}(s)) & kMask);
184 }
185 
186 }  // namespace trace_processor
187 }  // namespace perfetto
188