/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <limits> #include <stdint.h> #include "src/trace_processor/process_tracker.h" #include "src/trace_processor/slice_tracker.h" #include "src/trace_processor/trace_processor_context.h" #include "src/trace_processor/trace_storage.h" namespace perfetto { namespace trace_processor { namespace { // Slices which have been opened but haven't been closed yet will be marked // with this duration placeholder. constexpr int64_t kPendingDuration = -1; }; // namespace SliceTracker::SliceTracker(TraceProcessorContext* context) : context_(context) {} SliceTracker::~SliceTracker() = default; void SliceTracker::BeginAndroid(int64_t timestamp, uint32_t ftrace_tid, uint32_t atrace_tgid, StringId cat, StringId name) { UniqueTid utid = context_->process_tracker->UpdateThread(ftrace_tid, atrace_tgid); ftrace_to_atrace_tgid_[ftrace_tid] = atrace_tgid; Begin(timestamp, utid, cat, name); } void SliceTracker::Begin(int64_t timestamp, UniqueTid utid, StringId cat, StringId name) { MaybeCloseStack(timestamp, &threads_[utid]); StartSlice(timestamp, kPendingDuration, utid, cat, name); } void SliceTracker::Scoped(int64_t timestamp, UniqueTid utid, StringId cat, StringId name, int64_t duration) { PERFETTO_DCHECK(duration >= 0); MaybeCloseStack(timestamp, &threads_[utid]); StartSlice(timestamp, duration, utid, cat, name); } void SliceTracker::StartSlice(int64_t timestamp, int64_t duration, UniqueTid utid, StringId cat, StringId name) { auto* stack = &threads_[utid]; auto* slices = context_->storage->mutable_nestable_slices(); const uint8_t depth = static_cast<uint8_t>(stack->size()); if (depth >= std::numeric_limits<uint8_t>::max()) { PERFETTO_DFATAL("Slices with too large depth found."); return; } int64_t parent_stack_id = depth == 0 ? 0 : slices->stack_ids()[stack->back()]; size_t slice_idx = slices->AddSlice(timestamp, duration, utid, RefType::kRefUtid, cat, name, depth, 0, parent_stack_id); stack->emplace_back(slice_idx); slices->set_stack_id(slice_idx, GetStackHash(*stack)); } void SliceTracker::EndAndroid(int64_t timestamp, uint32_t ftrace_tid, uint32_t atrace_tgid) { auto actual_tgid_it = ftrace_to_atrace_tgid_.find(ftrace_tid); if (actual_tgid_it == ftrace_to_atrace_tgid_.end()) { // This is possible if we start tracing after a begin slice. PERFETTO_DLOG("Unknown tgid for ftrace tid %u", ftrace_tid); return; } uint32_t actual_tgid = actual_tgid_it->second; // atrace_tgid can be 0 in older android versions where the end event would // not contain the value. if (atrace_tgid != 0 && atrace_tgid != actual_tgid) { PERFETTO_DLOG("Mismatched atrace pid %u and looked up pid %u", atrace_tgid, actual_tgid); context_->storage->IncrementStats(stats::atrace_tgid_mismatch); } UniqueTid utid = context_->process_tracker->UpdateThread(ftrace_tid, actual_tgid); End(timestamp, utid); } void SliceTracker::End(int64_t timestamp, UniqueTid utid, StringId cat, StringId name) { MaybeCloseStack(timestamp, &threads_[utid]); const auto& stack = threads_[utid]; if (stack.empty()) return; auto* slices = context_->storage->mutable_nestable_slices(); size_t slice_idx = stack.back(); // If we are trying to close mismatching slices (e.g., slices that began // before tracing started), bail out. if (cat && slices->cats()[slice_idx] != cat) return; if (name && slices->names()[slice_idx] != name) return; PERFETTO_DCHECK(slices->durations()[slice_idx] == kPendingDuration); slices->set_duration(slice_idx, timestamp - slices->start_ns()[slice_idx]); CompleteSlice(utid); // TODO(primiano): auto-close B slices left open at the end. } void SliceTracker::CompleteSlice(UniqueTid utid) { threads_[utid].pop_back(); } void SliceTracker::MaybeCloseStack(int64_t ts, SlicesStack* stack) { const auto& slices = context_->storage->nestable_slices(); bool check_only = false; for (int i = static_cast<int>(stack->size()) - 1; i >= 0; i--) { size_t slice_idx = (*stack)[static_cast<size_t>(i)]; int64_t start_ts = slices.start_ns()[slice_idx]; int64_t dur = slices.durations()[slice_idx]; int64_t end_ts = start_ts + dur; if (dur == kPendingDuration) { check_only = true; } if (check_only) { PERFETTO_CHECK(ts >= start_ts); PERFETTO_CHECK(dur == kPendingDuration || ts <= end_ts); continue; } if (end_ts <= ts) { stack->pop_back(); } } } int64_t SliceTracker::GetStackHash(const SlicesStack& stack) { PERFETTO_DCHECK(!stack.empty()); const auto& slices = context_->storage->nestable_slices(); std::string s; s.reserve(stack.size() * sizeof(uint64_t) * 2); for (size_t i = 0; i < stack.size(); i++) { size_t slice_idx = stack[i]; s.append(reinterpret_cast<const char*>(&slices.cats()[slice_idx]), sizeof(slices.cats()[slice_idx])); s.append(reinterpret_cast<const char*>(&slices.names()[slice_idx]), sizeof(slices.names()[slice_idx])); } constexpr uint64_t kMask = uint64_t(-1) >> 1; return static_cast<int64_t>((std::hash<std::string>{}(s)) & kMask); } } // namespace trace_processor } // namespace perfetto