aboutsummaryrefslogtreecommitdiff
path: root/src/trace_processor/importers/proto/async_track_set_tracker.h
blob: b3dd836c8d15804f547b19b228e9860343601940 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
/*
 * 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;
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_