1 /*
2 * Copyright (C) 2024 The Android Open Source Project
3 *
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 #include "src/trace_redaction/process_thread_timeline.h"
18
19 #include <algorithm>
20 #include <cstddef>
21 #include <cstdint>
22
23 #include "perfetto/base/logging.h"
24
25 namespace perfetto::trace_redaction {
26 namespace {
27 // Limit the number of iterations to avoid an infinite loop. 10 is a generous
28 // number of iterations.
29 constexpr size_t kMaxSearchDepth = 10;
30
OrderByPid(const ProcessThreadTimeline::Event & left,const ProcessThreadTimeline::Event & right)31 bool OrderByPid(const ProcessThreadTimeline::Event& left,
32 const ProcessThreadTimeline::Event& right) {
33 return left.pid < right.pid;
34 }
35
36 } // namespace
37
Append(const Event & event)38 void ProcessThreadTimeline::Append(const Event& event) {
39 events_.push_back(event);
40 mode_ = Mode::kWrite;
41 }
42
Sort()43 void ProcessThreadTimeline::Sort() {
44 std::sort(events_.begin(), events_.end(), OrderByPid);
45 mode_ = Mode::kRead;
46 }
47
GetOpeningEvent(uint64_t ts,int32_t pid) const48 const ProcessThreadTimeline::Event* ProcessThreadTimeline::GetOpeningEvent(
49 uint64_t ts,
50 int32_t pid) const {
51 PERFETTO_DCHECK(mode_ == Mode::kRead);
52
53 auto prev_open = QueryLeftMax(ts, pid, Event::Type::kOpen);
54 auto prev_close = QueryLeftMax(ts, pid, Event::Type::kClose);
55
56 // If there is no open event before ts, it means pid never started.
57 if (!prev_open) {
58 return nullptr;
59 }
60
61 // There is a close event that is strictly between the open event and ts, then
62 // the pid is considered free.
63 //
64 // |---------| ^ : pid is free
65 // ^ |---------| ^ : pid is free
66 // ^---------| : pid is active
67 // |---------^ : pid is active
68 // |----^----| : pid is active
69
70 // Both open and close are less than or equal to ts (QueryLeftMax).
71 uint64_t close = prev_close ? prev_close->ts : 0;
72 uint64_t open = prev_open->ts;
73
74 return close > open && close < ts ? nullptr : prev_open;
75 }
76
PidConnectsToUid(uint64_t ts,int32_t pid,uint64_t uid) const77 bool ProcessThreadTimeline::PidConnectsToUid(uint64_t ts,
78 int32_t pid,
79 uint64_t uid) const {
80 PERFETTO_DCHECK(mode_ == Mode::kRead);
81
82 const auto* prev_open = QueryLeftMax(ts, pid, Event::Type::kOpen);
83 const auto* prev_close = QueryLeftMax(ts, pid, Event::Type::kClose);
84
85 for (size_t d = 0; d < kMaxSearchDepth; ++d) {
86 // If there's no previous open event, it means this pid was never created.
87 // This should not happen.
88 if (!prev_open) {
89 return false;
90 }
91
92 // This get tricky here. If done wrong, proc_free events will fail because
93 // they'll report as disconnected when they could be connected to the
94 // package. Inclusive bounds are used here. In context, if a task_newtask
95 // event happens at time T, the pid exists at time T. If a proc_free event
96 // happens at time T, the pid is "shutting down" at time T but still exists.
97 //
98 // B E : B = begin
99 // . . E = end
100 // . .
101 // |---------| ^ : pid is free
102 // ^ |---------| : pid is free
103 // ^---------| : pid is active
104 // |---------^ : pid is active
105 // |----^----| : pid is active
106
107 // By definition, both open and close are less than or equal to ts
108 // (QueryLeftMax), so problem space is reduces.
109 auto close = prev_close ? prev_close->ts : 0;
110 auto open = prev_open->ts;
111
112 if (close > open && close < ts) {
113 return false; // Close is sitting between open and ts.
114 }
115
116 // TODO(vaage): Normalize the uid values.
117 if (prev_open->uid == uid) {
118 return true;
119 }
120
121 if (prev_open->ppid == Event::kUnknownPid) {
122 return false; // If there is no parent, there is no way to keep
123 // searching.
124 }
125
126 auto ppid = prev_open->ppid;
127
128 prev_open = QueryLeftMax(ts, ppid, Event::Type::kOpen);
129 prev_close = QueryLeftMax(ts, ppid, Event::Type::kClose);
130 }
131
132 return false;
133 }
134
QueryLeftMax(uint64_t ts,int32_t pid,Event::Type type) const135 const ProcessThreadTimeline::Event* ProcessThreadTimeline::QueryLeftMax(
136 uint64_t ts,
137 int32_t pid,
138 Event::Type type) const {
139 auto fake = Event::Close(0, pid);
140
141 // Events are sorted by pid, creating islands of data. This search is to put
142 // the cursor at the start of pid's island. Each island will be small (a
143 // couple of items), so searching within the islands should be cheap.
144 auto it = std::lower_bound(events_.begin(), events_.end(), fake, OrderByPid);
145 auto end = std::upper_bound(events_.begin(), events_.end(), fake, OrderByPid);
146
147 const Event* best = nullptr;
148
149 for (; it != end; ++it) {
150 bool replace = false;
151
152 if (it->type == type && it->ts <= ts) {
153 replace = !best || it->ts > best->ts;
154 }
155
156 if (replace) {
157 best = &(*it);
158 }
159 }
160
161 return best;
162 }
163
164 } // namespace perfetto::trace_redaction
165