blob: b1ccf56e834290469a44929f15a6af2d9b8f9c17 [file] [log] [blame]
Primiano Tuccid933d912018-09-04 09:15:07 +01001/*
Primiano Tucci89795fd2019-02-18 23:08:06 +00002 * Copyright (C) 2018 The Android Open Source Project
Primiano Tuccid933d912018-09-04 09:15:07 +01003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef SRC_TRACE_PROCESSOR_TRACE_SORTER_H_
18#define SRC_TRACE_PROCESSOR_TRACE_SORTER_H_
19
Primiano Tucci81d0a392018-09-04 15:15:37 +010020#include <vector>
21
Primiano Tucci2c5488f2019-06-01 03:27:28 +010022#include "perfetto/ext/base/circular_queue.h"
Ioannis Ilkoseff38f52018-10-29 10:37:55 +000023#include "perfetto/trace_processor/basic_types.h"
Eric Seckler1a2ea132019-10-16 11:35:31 +010024#include "src/trace_processor/timestamped_trace_piece.h"
Primiano Tuccid933d912018-09-04 09:15:07 +010025#include "src/trace_processor/trace_blob_view.h"
26#include "src/trace_processor/trace_processor_context.h"
27#include "src/trace_processor/trace_storage.h"
28
Deepanjan Roy01994ca2019-04-02 11:05:34 -070029namespace Json {
Eric Seckler1a2ea132019-10-16 11:35:31 +010030class Value;
Deepanjan Roy01994ca2019-04-02 11:05:34 -070031} // namespace Json
Deepanjan Roy01994ca2019-04-02 11:05:34 -070032
Primiano Tuccid933d912018-09-04 09:15:07 +010033namespace perfetto {
34namespace trace_processor {
35
Eric Seckleree1cdea2019-10-22 12:13:06 +010036class FuchsiaProviderView;
Eric Seckler771960c2019-10-22 15:37:12 +010037class PacketSequenceState;
Eric Seckleree1cdea2019-10-22 12:13:06 +010038
Primiano Tucci81d0a392018-09-04 15:15:37 +010039// This class takes care of sorting events parsed from the trace stream in
40// arbitrary order and pushing them to the next pipeline stages (parsing) in
41// order. In order to support streaming use-cases, sorting happens within a
42// max window. Events are held in the TraceSorter staging area (events_) until
43// either (1) the (max - min) timestamp > window_size; (2) trace EOF.
44//
Primiano Tucci7de4ef22019-02-21 17:13:42 +000045// This class is designed around the assumption that:
46// - Most events come from ftrace.
47// - Ftrace events are sorted within each cpu most of the times.
Primiano Tucci81d0a392018-09-04 15:15:37 +010048//
Primiano Tucci7de4ef22019-02-21 17:13:42 +000049// Due to this, this class is oprerates as a streaming merge-sort of N+1 queues
50// (N = num cpus + 1 for non-ftrace events). Each queue in turn gets sorted (if
51// necessary) before proceeding with the global merge-sort-extract.
52// When an event is pushed through, it is just appeneded to the end of one of
53// the N queues. While appending, we keep track of the fact that the queue
54// is still ordered or just lost ordering. When an out-of-order event is
55// detected on a queue we keep track of: (1) the offset within the queue where
56// the chaos begun, (2) the timestamp that broke the ordering.
57// When we decide to extract events from the queues into the next stages of
58// the trace processor, we re-sort the events in the queue. Rather than
Primiano Tucci81d0a392018-09-04 15:15:37 +010059// re-sorting everything all the times, we use the above knowledge to restrict
60// sorting to the (hopefully smaller) tail of the |events_| staging area.
61// At any time, the first partition of |events_| [0 .. sort_start_idx_) is
62// ordered, and the second partition [sort_start_idx_.. end] is not.
63// We use a logarithmic bound search operation to figure out what is the index
64// within the first partition where sorting should start, and sort all events
65// from there to the end.
Primiano Tuccid933d912018-09-04 09:15:07 +010066class TraceSorter {
67 public:
Primiano Tucci7de4ef22019-02-21 17:13:42 +000068 TraceSorter(TraceProcessorContext*, int64_t window_size_ns);
Primiano Tuccid933d912018-09-04 09:15:07 +010069
Florian Mayer5716fc12019-06-24 11:50:51 -070070 inline void PushTracePacket(int64_t timestamp,
Eric Seckler771960c2019-10-22 15:37:12 +010071 PacketSequenceState* state,
Florian Mayer5716fc12019-06-24 11:50:51 -070072 TraceBlobView packet) {
Primiano Tucci7de4ef22019-02-21 17:13:42 +000073 DCHECK_ftrace_batch_cpu(kNoBatch);
74 auto* queue = GetQueue(0);
Florian Mayer5716fc12019-06-24 11:50:51 -070075 queue->Append(TimestampedTracePiece(timestamp, packet_idx_++,
Eric Seckler7e9dc312020-01-02 15:17:28 +000076 std::move(packet),
77 state->current_generation()));
Primiano Tucci7de4ef22019-02-21 17:13:42 +000078 MaybeExtractEvents(queue);
Primiano Tucci81d0a392018-09-04 15:15:37 +010079 }
80
Deepanjan Roy01994ca2019-04-02 11:05:34 -070081 inline void PushJsonValue(int64_t timestamp,
82 std::unique_ptr<Json::Value> json_value) {
83 auto* queue = GetQueue(0);
84 queue->Append(
85 TimestampedTracePiece(timestamp, packet_idx_++, std::move(json_value)));
86 MaybeExtractEvents(queue);
87 }
88
Eric Seckler67e15a92020-01-03 13:20:46 +000089 inline void PushFuchsiaRecord(int64_t timestamp,
90 std::unique_ptr<FuchsiaRecord> record) {
Brian Hamrickd57e1332019-04-24 11:25:36 -070091 DCHECK_ftrace_batch_cpu(kNoBatch);
92 auto* queue = GetQueue(0);
Eric Seckler67e15a92020-01-03 13:20:46 +000093 queue->Append(
94 TimestampedTracePiece(timestamp, packet_idx_++, std::move(record)));
Brian Hamrickd57e1332019-04-24 11:25:36 -070095 MaybeExtractEvents(queue);
96 }
97
Primiano Tucci7de4ef22019-02-21 17:13:42 +000098 inline void PushFtraceEvent(uint32_t cpu,
99 int64_t timestamp,
100 TraceBlobView event) {
101 set_ftrace_batch_cpu_for_DCHECK(cpu);
102 GetQueue(cpu + 1)->Append(
103 TimestampedTracePiece(timestamp, packet_idx_++, std::move(event)));
104
105 // The caller must call FinalizeFtraceEventBatch() after having pushed a
106 // batch of ftrace events. This is to amortize the overhead of handling
107 // global ordering and doing that in batches only after all ftrace events
108 // for a bundle are pushed.
Primiano Tucci81d0a392018-09-04 15:15:37 +0100109 }
Primiano Tuccid933d912018-09-04 09:15:07 +0100110
Ryan Savitskieb65cc62019-09-24 16:39:08 +0100111 // As with |PushFtraceEvent|, doesn't immediately sort the affected queues.
112 // TODO(rsavitski): if a trace has a mix of normal & "compact" events (being
113 // pushed through this function), the ftrace batches will no longer be fully
114 // sorted by timestamp. In such situations, we will have to sort at the end of
115 // the batch. We can do better as both sub-sequences are sorted however.
116 // Consider adding extra queues, or pushing them in a merge-sort fashion
117 // instead.
118 inline void PushInlineFtraceEvent(uint32_t cpu,
119 int64_t timestamp,
Eric Seckler67e15a92020-01-03 13:20:46 +0000120 InlineSchedSwitch inline_sched_switch) {
Ryan Savitskieb65cc62019-09-24 16:39:08 +0100121 set_ftrace_batch_cpu_for_DCHECK(cpu);
122 GetQueue(cpu + 1)->Append(
Eric Seckler67e15a92020-01-03 13:20:46 +0000123 TimestampedTracePiece(timestamp, packet_idx_++, inline_sched_switch));
124 }
125 inline void PushInlineFtraceEvent(uint32_t cpu,
126 int64_t timestamp,
127 InlineSchedWaking inline_sched_waking) {
128 set_ftrace_batch_cpu_for_DCHECK(cpu);
129 GetQueue(cpu + 1)->Append(
130 TimestampedTracePiece(timestamp, packet_idx_++, inline_sched_waking));
Ryan Savitskieb65cc62019-09-24 16:39:08 +0100131 }
132
Eric Seckler771960c2019-10-22 15:37:12 +0100133 inline void PushTrackEventPacket(int64_t timestamp,
134 int64_t thread_time,
135 int64_t thread_instruction_count,
136 PacketSequenceState* state,
137 TraceBlobView packet) {
Eric Seckler5526a752019-05-02 11:22:29 +0100138 auto* queue = GetQueue(0);
Eric Seckler7e9dc312020-01-02 15:17:28 +0000139 std::unique_ptr<TrackEventData> data(
140 new TrackEventData{std::move(packet), state->current_generation(),
141 thread_time, thread_instruction_count});
Eric Seckler67e15a92020-01-03 13:20:46 +0000142 queue->Append(
143 TimestampedTracePiece(timestamp, packet_idx_++, std::move(data)));
Eric Seckler5526a752019-05-02 11:22:29 +0100144 MaybeExtractEvents(queue);
Eric Seckler684a4f72019-04-26 14:34:07 +0100145 }
146
Primiano Tucci7de4ef22019-02-21 17:13:42 +0000147 inline void FinalizeFtraceEventBatch(uint32_t cpu) {
148 DCHECK_ftrace_batch_cpu(cpu);
149 set_ftrace_batch_cpu_for_DCHECK(kNoBatch);
150 MaybeExtractEvents(GetQueue(cpu + 1));
151 }
Primiano Tuccid933d912018-09-04 09:15:07 +0100152
Primiano Tucci7de4ef22019-02-21 17:13:42 +0000153 // Extract all events ignoring the window.
154 void ExtractEventsForced() {
155 SortAndExtractEventsBeyondWindow(/*window_size_ns=*/0);
Hector Dearman5ca4ab62019-11-28 17:34:15 +0000156 queues_.resize(0);
Primiano Tucci81d0a392018-09-04 15:15:37 +0100157 }
Primiano Tuccid933d912018-09-04 09:15:07 +0100158
Lalit Maganti295a8612019-05-21 13:57:42 +0100159 // Sets the window size to be the size specified (which should be lower than
160 // any previous window size specified) and flushes any data beyond
161 // this window size.
162 // It is undefined to call this function with a window size greater than than
163 // the current size.
164 void SetWindowSizeNs(int64_t window_size_ns) {
165 PERFETTO_DCHECK(window_size_ns <= window_size_ns_);
166
167 PERFETTO_DLOG("Setting window size to be %" PRId64 " ns", window_size_ns);
Primiano Tuccid933d912018-09-04 09:15:07 +0100168 window_size_ns_ = window_size_ns;
Lalit Maganti295a8612019-05-21 13:57:42 +0100169
170 // Fast path: if, globally, we are within the window size, then just exit.
171 if (global_max_ts_ - global_min_ts_ < window_size_ns)
172 return;
173 SortAndExtractEventsBeyondWindow(window_size_ns_);
Primiano Tuccid933d912018-09-04 09:15:07 +0100174 }
175
Eric Seckler5c7866e2019-10-18 08:38:16 +0100176 int64_t max_timestamp() const { return global_max_ts_; }
177
Primiano Tuccid933d912018-09-04 09:15:07 +0100178 private:
Primiano Tucci7de4ef22019-02-21 17:13:42 +0000179 static constexpr uint32_t kNoBatch = std::numeric_limits<uint32_t>::max();
Primiano Tucci81d0a392018-09-04 15:15:37 +0100180
Primiano Tucci7de4ef22019-02-21 17:13:42 +0000181 struct Queue {
182 inline void Append(TimestampedTracePiece ttp) {
183 const int64_t timestamp = ttp.timestamp;
184 events_.emplace_back(std::move(ttp));
185 min_ts_ = std::min(min_ts_, timestamp);
186
187 // Events are often seen in order.
188 if (PERFETTO_LIKELY(timestamp >= max_ts_)) {
189 max_ts_ = timestamp;
Primiano Tucci81d0a392018-09-04 15:15:37 +0100190 } else {
Primiano Tucci7de4ef22019-02-21 17:13:42 +0000191 // The event is breaking ordering. The first time it happens, keep
192 // track of which index we are at. We know that everything before that
193 // is sorted (because events were pushed monotonically). Everything
194 // after that index, instead, will need a sorting pass before moving
195 // events to the next pipeline stage.
196 if (sort_start_idx_ == 0) {
197 PERFETTO_DCHECK(events_.size() >= 2);
198 sort_start_idx_ = events_.size() - 1;
199 sort_min_ts_ = timestamp;
200 } else {
201 sort_min_ts_ = std::min(sort_min_ts_, timestamp);
202 }
Primiano Tucci81d0a392018-09-04 15:15:37 +0100203 }
Primiano Tucci7de4ef22019-02-21 17:13:42 +0000204
205 PERFETTO_DCHECK(min_ts_ <= max_ts_);
Primiano Tucci81d0a392018-09-04 15:15:37 +0100206 }
207
Primiano Tucci7de4ef22019-02-21 17:13:42 +0000208 bool needs_sorting() const { return sort_start_idx_ != 0; }
209 void Sort();
Primiano Tucci81d0a392018-09-04 15:15:37 +0100210
Primiano Tucci7de4ef22019-02-21 17:13:42 +0000211 base::CircularQueue<TimestampedTracePiece> events_;
212 int64_t min_ts_ = std::numeric_limits<int64_t>::max();
213 int64_t max_ts_ = 0;
214 size_t sort_start_idx_ = 0;
Deepanjan Roy01994ca2019-04-02 11:05:34 -0700215 int64_t sort_min_ts_ = std::numeric_limits<int64_t>::max();
Primiano Tucci7de4ef22019-02-21 17:13:42 +0000216 };
Primiano Tucci81d0a392018-09-04 15:15:37 +0100217
Primiano Tucci7de4ef22019-02-21 17:13:42 +0000218 // This method passes any events older than window_size_ns to the
219 // parser to be parsed and then stored.
220 void SortAndExtractEventsBeyondWindow(int64_t windows_size_ns);
Primiano Tucci81d0a392018-09-04 15:15:37 +0100221
Primiano Tucci7de4ef22019-02-21 17:13:42 +0000222 inline Queue* GetQueue(size_t index) {
223 if (PERFETTO_UNLIKELY(index >= queues_.size()))
224 queues_.resize(index + 1);
225 return &queues_[index];
Primiano Tucci81d0a392018-09-04 15:15:37 +0100226 }
227
Primiano Tucci7de4ef22019-02-21 17:13:42 +0000228 inline void MaybeExtractEvents(Queue* queue) {
229 DCHECK_ftrace_batch_cpu(kNoBatch);
230 global_max_ts_ = std::max(global_max_ts_, queue->max_ts_);
231 global_min_ts_ = std::min(global_min_ts_, queue->min_ts_);
232
Lalit Maganti295a8612019-05-21 13:57:42 +0100233 // Fast path: if, globally, we are within the window size, then just exit.
Primiano Tucci7de4ef22019-02-21 17:13:42 +0000234 if (global_max_ts_ - global_min_ts_ < window_size_ns_)
235 return;
Primiano Tucci7de4ef22019-02-21 17:13:42 +0000236 SortAndExtractEventsBeyondWindow(window_size_ns_);
237 }
238
Primiano Tuccid933d912018-09-04 09:15:07 +0100239 TraceProcessorContext* const context_;
Primiano Tucci7de4ef22019-02-21 17:13:42 +0000240
241 // queues_[0] is the general (non-ftrace) queue.
242 // queues_[1] is the ftrace queue for CPU(0).
243 // queues_[x] is the ftrace queue for CPU(x - 1).
244 std::vector<Queue> queues_;
Primiano Tucci81d0a392018-09-04 15:15:37 +0100245
246 // Events are propagated to the next stage only after (max - min) timestamp
247 // is larger than this value.
Lalit Maganti85ca4a82018-12-07 17:28:02 +0000248 int64_t window_size_ns_;
Primiano Tucci81d0a392018-09-04 15:15:37 +0100249
Primiano Tucci7de4ef22019-02-21 17:13:42 +0000250 // max(e.timestamp for e in queues_).
251 int64_t global_max_ts_ = 0;
Primiano Tucci81d0a392018-09-04 15:15:37 +0100252
Primiano Tucci7de4ef22019-02-21 17:13:42 +0000253 // min(e.timestamp for e in queues_).
254 int64_t global_min_ts_ = std::numeric_limits<int64_t>::max();
Primiano Tucci81d0a392018-09-04 15:15:37 +0100255
Lalit Magantifa21a282019-01-17 19:03:04 +0000256 // Monotonic increasing value used to index timestamped trace pieces.
Primiano Tucci7de4ef22019-02-21 17:13:42 +0000257 uint64_t packet_idx_ = 0;
Lalit Magantifa21a282019-01-17 19:03:04 +0000258
Primiano Tucci7de4ef22019-02-21 17:13:42 +0000259 // Used for performance tests. True when setting TRACE_PROCESSOR_SORT_ONLY=1.
260 bool bypass_next_stage_for_testing_ = false;
Primiano Tucci81d0a392018-09-04 15:15:37 +0100261
Primiano Tucci7de4ef22019-02-21 17:13:42 +0000262#if PERFETTO_DCHECK_IS_ON()
263 // Used only for DCHECK-ing that FinalizeFtraceEventBatch() is called.
264 uint32_t ftrace_batch_cpu_ = kNoBatch;
265
266 inline void DCHECK_ftrace_batch_cpu(uint32_t cpu) {
267 PERFETTO_DCHECK(ftrace_batch_cpu_ == kNoBatch || ftrace_batch_cpu_ == cpu);
268 }
269
270 inline void set_ftrace_batch_cpu_for_DCHECK(uint32_t cpu) {
271 PERFETTO_DCHECK(ftrace_batch_cpu_ == cpu || ftrace_batch_cpu_ == kNoBatch ||
272 cpu == kNoBatch);
273 ftrace_batch_cpu_ = cpu;
274 }
275#else
276 inline void DCHECK_ftrace_batch_cpu(uint32_t) {}
277 inline void set_ftrace_batch_cpu_for_DCHECK(uint32_t) {}
278#endif
Primiano Tuccid933d912018-09-04 09:15:07 +0100279};
280
281} // namespace trace_processor
282} // namespace perfetto
283
284#endif // SRC_TRACE_PROCESSOR_TRACE_SORTER_H_