blob: 6e7fa8140d45ca64184e7909d6da3f4e4f964e97 [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.
*/
#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_ASYNC_TRACK_SET_TRACKER_H_
#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_ASYNC_TRACK_SET_TRACKER_H_
#include "src/trace_processor/storage/trace_storage.h"
namespace perfetto {
namespace trace_processor {
class TraceProcessorContext;
class AsyncTrackSetTrackerUnittest;
// Tracker used to reduce the number of trace processor tracks corresponding
// to a single "UI track".
//
// UIs using trace processor want to display all slices in the same context
// (e.g. same upid) and same name into a single track. However, because trace
// processor does not allow parallel slices on a single track (because it breaks
// things like span join, self time computation etc.), at the trace processor
// level these parallel slices are put on different tracks.
//
// Creating a new track for every event, however, leads to an explosion of
// tracks which is undesirable. This class exists to multiplex slices so that
// n events correspond to a single track in a way which minimises the number of
// tracks which needs to be merged by the UI.
//
// The intended usage of this class is for callers to first call one of the
// Intern* methods to obtain a TrackSetId followed by Begin/End just before
// calling into SliceTracker's Begin/End respectively. For example:
// TrackSetId set_id = track_set_tracker->InternAndroidSet(upid, name);
// if (event.begin) {
// TrackId id = track_set_tracker->Begin(set_id, cookie);
// slice_tracker->Begin(ts, id, ...)
// } else {
// ... (same thing with end)
// }
// Alternatively, instead of Begin/End, Scoped can also be called if supported
// by the track type.
class AsyncTrackSetTracker {
public:
using TrackSetId = uint32_t;
explicit AsyncTrackSetTracker(TraceProcessorContext* context);
~AsyncTrackSetTracker() = default;
// Interns a set of global async slice tracks associated with the given name.
TrackSetId InternGlobalTrackSet(StringId name);
// Interns a set of Android async slice tracks associated with the given
// upid and name.
// Scoped is *not* supported for this track set type.
TrackSetId InternAndroidSet(UniquePid, StringId name);
// Starts a new slice on the given async track set which has the given cookie.
TrackId Begin(TrackSetId id, int64_t cookie);
// Interns the expected and actual timeline tracks coming from FrameTimeline
// producer for the associated upid.
TrackSetId InternFrameTimelineSet(UniquePid, StringId name);
// Ends a new slice on the given async track set which has the given cookie.
TrackId End(TrackSetId id, int64_t cookie);
// Creates a scoped slice on the given async track set.
// This method makes sure that any other slice in this track set does
// not happen simultaneously on the returned track.
// Only supported on selected track set types; read the documentation for
// the Intern* method for your track type to check if supported.
TrackId Scoped(TrackSetId id, int64_t ts, int64_t dur);
private:
friend class AsyncTrackSetTrackerUnittest;
struct AndroidTuple {
UniquePid upid;
StringId name;
friend bool operator<(const AndroidTuple& l, const AndroidTuple& r) {
return std::tie(l.upid, l.name) < std::tie(r.upid, r.name);
}
};
struct FrameTimelineTuple {
UniquePid upid;
StringId name;
friend bool operator<(const FrameTimelineTuple& l,
const FrameTimelineTuple& r) {
return std::tie(l.upid, l.name) < std::tie(r.upid, r.name);
}
};
// Indicates the nesting behaviour of slices associated to a single slice
// stack.
enum class NestingBehaviour {
// Indicates that slices are unnestable; that is, it is an error
// to call Begin -> Begin with a single cookie without End inbetween.
// This pattern should be the default behaviour that most async slices
// should use.
kUnnestable,
// Indicates that slices are unnestable but also saturating; that is
// calling Begin -> Begin only causes a single Begin to be recorded.
// This is only really useful for Android async slices which have this
// behaviour for legacy reasons. See the comment in
// SystraceParser::ParseSystracePoint for information on why
// this behaviour exists.
kLegacySaturatingUnnestable,
};
enum class TrackSetType {
kGlobal,
kAndroid,
kFrameTimeline,
};
struct TrackState {
TrackId id;
enum class SliceType { kCookie, kTimestamp };
SliceType slice_type;
union {
// Only valid for |slice_type| == |SliceType::kCookie|.
int64_t cookie;
// Only valid for |slice_type| == |SliceType::kTimestamp|.
int64_t ts_end;
};
// Only used for |slice_type| == |SliceType::kCookie|.
uint32_t nest_count;
};
struct TrackSet {
TrackSetType type;
union {
StringId global_track_name;
// Only set when |type| == |TrackSetType::kAndroid|.
AndroidTuple android_tuple;
// Only set when |type| == |TrackSetType::kFrameTimeline|.
FrameTimelineTuple frame_timeline_tuple;
};
NestingBehaviour nesting_behaviour;
std::vector<TrackState> tracks;
};
TrackSetId CreateUnnestableTrackSetForTesting(UniquePid upid, StringId name) {
AsyncTrackSetTracker::TrackSet set;
set.android_tuple = AndroidTuple{upid, name};
set.type = AsyncTrackSetTracker::TrackSetType::kAndroid;
set.nesting_behaviour = NestingBehaviour::kUnnestable;
track_sets_.emplace_back(set);
return static_cast<TrackSetId>(track_sets_.size() - 1);
}
// Returns the state for a track using the following algorithm:
// 1. If a track exists with the given cookie in the track set, returns
// that track.
// 2. Otherwise, looks for any track in the set which is "open" (i.e.
// does not have another slice currently scheduled).
// 3. Otherwise, creates a new track and associates it with the set.
TrackState& GetOrCreateTrackForCookie(TrackSet& set, int64_t cookie);
TrackId CreateTrackForSet(const TrackSet& set);
std::map<StringId, TrackSetId> global_track_set_ids_;
std::map<AndroidTuple, TrackSetId> android_track_set_ids_;
std::map<FrameTimelineTuple, TrackSetId> frame_timeline_track_set_ids_;
std::vector<TrackSet> track_sets_;
TraceProcessorContext* const context_;
};
} // namespace trace_processor
} // namespace perfetto
#endif // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_ASYNC_TRACK_SET_TRACKER_H_