blob: 92c631143b97042e7e47a65c662abab393ab01dd [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 <unordered_map>
#include "src/trace_processor/storage/trace_storage.h"
namespace perfetto {
namespace trace_processor {
class TraceProcessorContext;
// 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)
// }
class AsyncTrackSetTracker {
public:
using TrackSetId = uint32_t;
// Indicates the nesting behaviour of slices associated to a single
// cookie.
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,
};
explicit AsyncTrackSetTracker(TraceProcessorContext* context);
~AsyncTrackSetTracker() = default;
// Interns a set of Android async slice tracks assocaited with the given
// upid and name.
TrackSetId InternAndroidSet(UniquePid, StringId name);
// Starts a new slice on the given async track set which has the given cookie
// and nesting behaviour.
TrackId Begin(TrackSetId id,
int64_t cookie,
NestingBehaviour = NestingBehaviour::kUnnestable);
// Ends a new slice on the given async track set which has the given cookie
// and nesting behaviour.
TrackId End(TrackSetId id, int64_t cookie);
private:
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);
}
};
enum class TrackSetType {
kAndroid,
};
struct TrackState {
TrackId id;
int64_t cookie;
uint32_t nest_count;
};
struct TrackSet {
TrackSetType type;
union {
// Only set when |type| == |TrackSetType::kAndroid|.
AndroidTuple android_tuple;
};
std::vector<TrackState> tracks;
};
// Gets an empty track (i.e. a track with no event started on it) or creates a
// new track in the set if none exists.
// Returns the index to the associated TrackState in the |tracks| vector.
uint32_t GetOrCreateEmptyTrack(TrackSet& set, int64_t cookie);
std::map<AndroidTuple, TrackSetId> android_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_