blob: 16dbc3c8efc2338987d387f2562e0844827985f9 [file] [log] [blame]
/*
* 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/slice_tracker.h"
#include "src/trace_processor/trace_processor_context.h"
#include "src/trace_processor/trace_storage.h"
namespace perfetto {
namespace trace_processor {
SliceTracker::SliceTracker(TraceProcessorContext* context)
: context_(context) {}
SliceTracker::~SliceTracker() = default;
void SliceTracker::Begin(uint64_t timestamp,
UniqueTid utid,
StringId cat,
StringId name) {
auto& stack = threads_[utid];
MaybeCloseStack(timestamp, stack);
stack.emplace_back(Slice{cat, name, timestamp, 0});
}
void SliceTracker::Scoped(uint64_t timestamp,
UniqueTid utid,
StringId cat,
StringId name,
uint64_t duration) {
auto& stack = threads_[utid];
MaybeCloseStack(timestamp, stack);
stack.emplace_back(Slice{cat, name, timestamp, timestamp + duration});
CompleteSlice(utid);
}
void SliceTracker::End(uint64_t timestamp,
UniqueTid utid,
StringId cat,
StringId name) {
auto& stack = threads_[utid];
MaybeCloseStack(timestamp, stack);
if (stack.empty()) {
return;
}
PERFETTO_CHECK(cat == 0 || stack.back().cat_id == cat);
PERFETTO_CHECK(name == 0 || stack.back().name_id == name);
Slice& slice = stack.back();
slice.end_ts = timestamp;
CompleteSlice(utid);
// TODO(primiano): auto-close B slices left open at the end.
}
void SliceTracker::CompleteSlice(UniqueTid utid) {
auto& stack = threads_[utid];
if (stack.size() >= std::numeric_limits<uint8_t>::max()) {
stack.pop_back();
return;
}
const uint8_t depth = static_cast<uint8_t>(stack.size()) - 1;
uint64_t parent_stack_id, stack_id;
std::tie(parent_stack_id, stack_id) = GetStackHashes(stack);
Slice& slice = stack.back();
auto* slices = context_->storage->mutable_nestable_slices();
slices->AddSlice(slice.start_ts, slice.end_ts - slice.start_ts, utid, 0,
slice.name_id, depth, stack_id, parent_stack_id);
stack.pop_back();
}
void SliceTracker::MaybeCloseStack(uint64_t ts, SlicesStack& stack) {
bool check_only = false;
for (int i = static_cast<int>(stack.size()) - 1; i >= 0; i--) {
const Slice& slice = stack[size_t(i)];
if (slice.end_ts == 0) {
check_only = true;
}
if (check_only) {
PERFETTO_DCHECK(ts >= slice.start_ts);
PERFETTO_DCHECK(slice.end_ts == 0 || ts <= slice.end_ts);
continue;
}
if (slice.end_ts <= ts) {
stack.pop_back();
}
}
}
// Returns <parent_stack_id, stack_id>, where
// |parent_stack_id| == hash(stack_id - last slice).
std::tuple<uint64_t, uint64_t> SliceTracker::GetStackHashes(
const SlicesStack& stack) {
PERFETTO_DCHECK(!stack.empty());
std::string s;
s.reserve(stack.size() * sizeof(uint64_t) * 2);
constexpr uint64_t kMask = uint64_t(-1) >> 1;
uint64_t parent_stack_id = 0;
for (size_t i = 0; i < stack.size(); i++) {
if (i == stack.size() - 1)
parent_stack_id = i > 0 ? (std::hash<std::string>{}(s)) & kMask : 0;
const Slice& slice = stack[i];
s.append(reinterpret_cast<const char*>(&slice.cat_id),
sizeof(slice.cat_id));
s.append(reinterpret_cast<const char*>(&slice.name_id),
sizeof(slice.name_id));
}
uint64_t stack_id = (std::hash<std::string>{}(s)) & kMask;
return std::make_tuple(parent_stack_id, stack_id);
}
} // namespace trace_processor
} // namespace perfetto