blob: 43d36372e925d25ca2d1e5a5490cafb0defc8349 [file]
/*
* Copyright (C) 2024 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.
*/
#include "src/trace_redaction/process_thread_timeline.h"
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include "perfetto/base/logging.h"
namespace perfetto::trace_redaction {
namespace {
// Limit the number of iterations to avoid an infinite loop. 10 is a generous
// number of iterations.
constexpr size_t kMaxSearchDepth = 10;
bool OrderByPid(const ProcessThreadTimeline::Event& left,
const ProcessThreadTimeline::Event& right) {
return left.pid < right.pid;
}
} // namespace
void ProcessThreadTimeline::Append(const Event& event) {
events_.push_back(event);
mode_ = Mode::kWrite;
}
void ProcessThreadTimeline::Sort() {
std::sort(events_.begin(), events_.end(), OrderByPid);
mode_ = Mode::kRead;
}
bool ProcessThreadTimeline::PidConnectsToUid(uint64_t ts,
int32_t pid,
uint64_t uid) const {
PERFETTO_DCHECK(mode_ == Mode::kRead);
auto event = FindPreviousEvent(ts, pid);
for (size_t d = 0; d < kMaxSearchDepth; ++d) {
// The thread/process was freed. It won't exist until a new open event.
if (event.type != Event::Type::kOpen) {
return false;
}
// TODO(vaage): Normalize the uid values.
if (event.uid == uid) {
return true;
}
// If there is no parent, there is no way to keep searching.
if (event.ppid == Event::kUnknownPid) {
return false;
}
event = FindPreviousEvent(ts, event.ppid);
}
return false;
}
ProcessThreadTimeline::Event ProcessThreadTimeline::FindPreviousEvent(
uint64_t ts,
int32_t pid) const {
PERFETTO_DCHECK(mode_ == Mode::kRead);
Event fake = Event::Close(ts, pid);
// Events are sorted by pid, creating islands of data. This search is to put
// the cursor at the start of pid's island. Each island will be small (a
// couple of items), so searching within the islands should be cheap.
auto at = std::lower_bound(events_.begin(), events_.end(), fake, OrderByPid);
// `pid` was not found in `events_`.
if (at == events_.end()) {
return {};
}
// "no best option".
Event best = {};
// Run through all events (related to this pid) and find the last event that
// comes before ts. If the events were in order by time, the search could be
// more efficient, but the gains are margin because:
//
// 1. The number of edge cases go up.
//
// 2. The code is harder to read.
//
// 3. The performance gains are minimal or non-existant because of the small
// number of events.
for (; at != events_.end() && at->pid == pid; ++at) {
// This event is after "now" and can safely be ignored.
if (at->ts > ts) {
continue;
}
// `at` is know to be before now. So it is always safe to accept an event.
//
// All ts values are positive. However, ts_at and ts_best are both less than
// ts (see early condition), meaning they can be considered negative values.
//
// at best ts
// <---+-----------+-------------+---->
// 31 64 93
//
// at best ts
// <---+-----------+-------------+---->
// -62 -29 0
//
// This means that the latest ts value under ts is the closest to ts.
if (best.type == Event::Type::kInvalid || at->ts > best.ts) {
best = *at;
}
// This handles the rare edge case where an open and close event occur at
// the same time. The close event must get priority. This is done by
// allowing close events to use ">=" where as other events can only use ">".
if (at->type == Event::Type::kClose && at->ts == best.ts) {
best = *at;
}
}
return best;
}
} // namespace perfetto::trace_redaction