blob: 432bd395f03b99904f94c988d53d30e504994611 [file] [log] [blame]
/*
* Copyright (C) 2020 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 "src/trace_processor/importers/common/async_track_set_tracker.h"
#include "src/trace_processor/importers/common/track_tracker.h"
#include "src/trace_processor/storage/trace_storage.h"
#include "src/trace_processor/types/trace_processor_context.h"
namespace perfetto {
namespace trace_processor {
AsyncTrackSetTracker::AsyncTrackSetTracker(TraceProcessorContext* context)
: android_source_(context->storage->InternString("android")),
context_(context) {}
AsyncTrackSetTracker::TrackSetId AsyncTrackSetTracker::InternGlobalTrackSet(
StringId name) {
auto it = global_track_set_ids_.find(name);
if (it != global_track_set_ids_.end()) {
return it->second;
}
uint32_t id = static_cast<uint32_t>(track_sets_.size());
TrackSet set;
set.global_track_name = name;
set.scope = TrackSetScope::kGlobal;
set.nesting_behaviour = NestingBehaviour::kNestable;
track_sets_.emplace_back(set);
return global_track_set_ids_[name] = id;
}
AsyncTrackSetTracker::TrackSetId AsyncTrackSetTracker::InternProcessTrackSet(
UniquePid upid,
StringId name) {
ProcessTuple tuple{upid, name};
auto it = process_track_set_ids_.find(tuple);
if (it != process_track_set_ids_.end())
return it->second;
uint32_t id = static_cast<uint32_t>(track_sets_.size());
TrackSet set;
set.process_tuple = tuple;
set.scope = TrackSetScope::kProcess;
set.nesting_behaviour = NestingBehaviour::kNestable;
track_sets_.emplace_back(set);
process_track_set_ids_[tuple] = id;
return id;
}
AsyncTrackSetTracker::TrackSetId
AsyncTrackSetTracker::InternAndroidLegacyUnnestableTrackSet(UniquePid upid,
StringId name) {
ProcessTuple tuple{upid, name};
auto it = android_legacy_unnestable_track_set_ids_.find(tuple);
if (it != android_legacy_unnestable_track_set_ids_.end())
return it->second;
uint32_t id = static_cast<uint32_t>(track_sets_.size());
TrackSet set;
set.process_tuple = tuple;
set.scope = TrackSetScope::kProcess;
set.nesting_behaviour = NestingBehaviour::kLegacySaturatingUnnestable;
track_sets_.emplace_back(set);
android_legacy_unnestable_track_set_ids_[tuple] = id;
return id;
}
TrackId AsyncTrackSetTracker::Begin(TrackSetId id, int64_t cookie) {
PERFETTO_DCHECK(id < track_sets_.size());
TrackSet& set = track_sets_[id];
TrackState& state = GetOrCreateTrackForCookie(set, cookie);
switch (set.nesting_behaviour) {
case NestingBehaviour::kNestable:
state.nest_count++;
break;
case NestingBehaviour::kLegacySaturatingUnnestable:
PERFETTO_DCHECK(state.nest_count <= 1);
state.nest_count = 1;
break;
}
return state.id;
}
TrackId AsyncTrackSetTracker::End(TrackSetId id, int64_t cookie) {
PERFETTO_DCHECK(id < track_sets_.size());
TrackSet& set = track_sets_[id];
TrackState& state = GetOrCreateTrackForCookie(set, cookie);
// It's possible to have a nest count of 0 even when we know about the track.
// Suppose the following sequence of events for some |id| and |cookie|:
// Begin
// (trace starts)
// Begin
// End
// End <- nest count == 0 here even though we have a record of this track.
if (state.nest_count > 0)
state.nest_count--;
return state.id;
}
TrackId AsyncTrackSetTracker::Scoped(TrackSetId id, int64_t ts, int64_t dur) {
PERFETTO_DCHECK(id < track_sets_.size());
TrackSet& set = track_sets_[id];
PERFETTO_DCHECK(set.nesting_behaviour !=
NestingBehaviour::kLegacySaturatingUnnestable);
auto it = std::find_if(
set.tracks.begin(), set.tracks.end(), [ts](const TrackState& state) {
return state.slice_type == TrackState::SliceType::kTimestamp &&
state.ts_end <= ts;
});
if (it != set.tracks.end()) {
it->ts_end = ts + dur;
return it->id;
}
TrackState state;
state.slice_type = TrackState::SliceType::kTimestamp;
state.ts_end = ts + dur;
state.id = CreateTrackForSet(set);
set.tracks.emplace_back(state);
return state.id;
}
AsyncTrackSetTracker::TrackState&
AsyncTrackSetTracker::GetOrCreateTrackForCookie(TrackSet& set, int64_t cookie) {
auto it = std::find_if(
set.tracks.begin(), set.tracks.end(), [cookie](const TrackState& state) {
return state.slice_type == TrackState::SliceType::kCookie &&
state.cookie == cookie;
});
if (it != set.tracks.end())
return *it;
it = std::find_if(
set.tracks.begin(), set.tracks.end(), [](const TrackState& state) {
return state.slice_type == TrackState::SliceType::kCookie &&
state.nest_count == 0;
});
if (it != set.tracks.end()) {
// Adopt this track for the cookie to make sure future slices with this
// cookie also get associated to this track.
it->cookie = cookie;
return *it;
}
TrackState state;
state.id = CreateTrackForSet(set);
state.slice_type = TrackState::SliceType::kCookie;
state.cookie = cookie;
state.nest_count = 0;
set.tracks.emplace_back(state);
return set.tracks.back();
}
TrackId AsyncTrackSetTracker::CreateTrackForSet(const TrackSet& set) {
switch (set.scope) {
case TrackSetScope::kGlobal:
// TODO(lalitm): propogate source from callers rather than just passing
// kNullStringId here.
return context_->track_tracker->CreateGlobalAsyncTrack(
set.global_track_name, kNullStringId);
case TrackSetScope::kProcess:
// TODO(lalitm): propogate source from callers rather than just passing
// kNullStringId here.
StringId source =
set.nesting_behaviour == NestingBehaviour::kLegacySaturatingUnnestable
? android_source_
: kNullStringId;
return context_->track_tracker->CreateProcessAsyncTrack(
set.process_tuple.name, set.process_tuple.upid, source);
}
PERFETTO_FATAL("For GCC");
}
} // namespace trace_processor
} // namespace perfetto