tracing: wip i am going to regret not splitting this right now #1

Closed
jade wants to merge 4 commits from jade/meow into main AGit
589 changed files with 209116 additions and 42 deletions

View file

@ -41,7 +41,7 @@
project('lix', 'cpp',
version : run_command('bash', '-c', 'echo -n $(jq -r .version < ./version.json)$VERSION_SUFFIX', check : true).stdout().strip(),
default_options : [
'cpp_std=c++2a',
'cpp_std=c++23',
# TODO(Qyriad): increase the warning level
'warning_level=1',
'debug=true',

View file

@ -18,10 +18,12 @@
#include "fetch-to-store.hh"
#include "flake/flakeref.hh"
#include "parser-tab.hh"
#include "fast-function-tracer.hh"
#include <algorithm>
#include <chrono>
#include <iostream>
#include <memory>
#include <sstream>
#include <cstring>
#include <optional>
@ -394,6 +396,7 @@ EvalState::EvalState(
, debugStop(false)
, trylevel(0)
, regexCache(makeRegexCache())
, tracer(nullptr)
#if HAVE_BOEHMGC
, valueAllocCache(std::allocate_shared<void *>(traceable_allocator<void *>(), nullptr))
, env1AllocCache(std::allocate_shared<void *>(traceable_allocator<void *>(), nullptr))
@ -441,6 +444,11 @@ EvalState::EvalState(
}
createBaseEnv();
// TODO: do this sensibly
if (auto traceFunctionsEnv = getEnv("NIX_TRACE_FUNCTIONS_FILE"); traceFunctionsEnv.has_value()) {
tracer = std::make_unique<FastFunctionTracer>(*this, *traceFunctionsEnv);
}
}
@ -1542,6 +1550,9 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
auto trace = evalSettings.traceFunctionCalls
? std::make_unique<FunctionCallTrace>(positions[pos])
: nullptr;
auto fastTraceTs = tracer ? std::optional{tracing::Timestamp::now()} : std::nullopt;
// TODO: this should never need to be a pointer to begin with! References are bad.
std::unique_ptr<FastFunctionTracer::Guard> fastTraceGuard = nullptr;
forceValue(fun, pos);
@ -1565,6 +1576,10 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
ExprLambda & lambda(*vCur.lambda.fun);
if (tracer && !fastTraceGuard) {
fastTraceGuard = std::make_unique<FastFunctionTracer::Guard>(tracer->enterFunction(lambda.name, lambda.pos, *fastTraceTs));
}
auto size =
(!lambda.arg ? 0 : 1) +
(lambda.hasFormals() ? lambda.formals->formals.size() : 0);
@ -1678,6 +1693,10 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
/* We have all the arguments, so call the primop. */
auto * fn = vCur.primOp;
if (tracer && !fastTraceGuard) {
fastTraceGuard = std::make_unique<FastFunctionTracer::Guard>(tracer->enterFunction(symbols.create(fn->name), PosIdx(), *fastTraceTs));
}
nrPrimOpCalls++;
if (countCalls) primOpCalls[fn->name]++;
@ -1712,7 +1731,6 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
} else {
/* We have all the arguments, so call the primop with
the previous and new arguments. */
Value * vArgs[maxPrimOpArity];
auto n = argsDone;
for (Value * arg = &vCur; arg->isPrimOpApp(); arg = arg->primOpApp.left)
@ -1725,6 +1743,10 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value &
nrPrimOpCalls++;
if (countCalls) primOpCalls[fn->name]++;
if (tracer && !fastTraceGuard) {
fastTraceGuard = std::make_unique<FastFunctionTracer::Guard>(tracer->enterFunction(symbols.create(fn->name), PosIdx(), *fastTraceTs));
}
try {
// TODO:
// 1. Unify this and above code. Heavily redundant.
@ -2760,6 +2782,12 @@ Expr * EvalState::parseExprFromFile(const SourcePath & path)
Expr * EvalState::parseExprFromFile(const SourcePath & path, std::shared_ptr<StaticEnv> & staticEnv)
{
// TODO: turning these into symbols is kind of unfortunate because it leaks memory
// TODO: This API loses the category of the things!
std::optional<FastFunctionTracer::Guard> _g = tracer
? std::optional{tracer->enterFunction(symbols.create(path.to_string()), PosIdx())}
: std::nullopt;
auto buffer = path.readFile();
// readFile hopefully have left some extra space for terminators
buffer.append("\0\0", 2);

View file

@ -14,6 +14,7 @@
#include "repl-exit-status.hh"
#include <map>
#include <memory>
#include <optional>
#include <unordered_map>
#include <mutex>
@ -34,6 +35,7 @@ class StorePath;
struct SingleDerivedPath;
enum RepairFlag : bool;
class FastFunctionTracer;
/**
* Function that implements a primop.
@ -274,6 +276,11 @@ private:
*/
std::shared_ptr<RegexCache> regexCache;
/**
* A function tracer. May be null!
*/
std::unique_ptr<FastFunctionTracer> tracer;
#if HAVE_BOEHMGC
/**
* Allocation cache for GC'd Value objects.

View file

@ -0,0 +1,118 @@
#include "fast-function-tracer.hh"
#include "eval.hh"
#include "pos-idx.hh"
#include "symbol-table.hh"
#include "trace-writer.hh"
#include "types.hh"
#include <fstream>
#include <memory>
namespace nix {
using std::operator""s;
class EvalStateRetriever : public tracing::InternedRetriever
{
public:
EvalState & evalState_;
EvalStateRetriever(EvalState & evalState) : evalState_(evalState) {}
virtual auto getName(tracing::InternedName idx) const -> std::string override;
virtual auto getPos(tracing::InternedPos idx, std::string const funcName) const
-> tracing::TracePos override;
virtual ~EvalStateRetriever() = default;
};
class FastFunctionTracer::Impl
{
public:
tracing::TraceWriter writer_;
Path outputPath_;
tracing::TraceWriter::Track rootTrack_;
Impl(EvalState & evalState, Path outputFile)
: writer_{std::make_unique<EvalStateRetriever>(evalState)}
, outputPath_{outputFile}
, rootTrack_{writer_.trackByTid(getpid(), gettid(), "Functions")}
{
}
};
FastFunctionTracer::FastFunctionTracer(EvalState & evalState, Path outputFile)
: impl_{std::make_unique<Impl>(evalState, std::move(outputFile))}
{
}
FastFunctionTracer::~FastFunctionTracer()
{
try {
std::ofstream ofs(impl_->outputPath_);
auto buf = impl_->writer_.splat();
ofs.write(reinterpret_cast<const char *>(buf.data()), buf.size());
} catch (std::exception & e) {
std::cerr << "Exception ignored while writing trace file: " << e.what();
}
}
FastFunctionTracer::Guard
FastFunctionTracer::enterFunction(const Symbol sym, const PosIdx pos, tracing::Timestamp ts)
{
return Guard{*this, ts, sym, pos};
}
FastFunctionTracer::Guard::Guard(
FastFunctionTracer & tracer, tracing::Timestamp ts, const Symbol sym, const PosIdx idx
)
: tracer_(tracer)
{
tracer.impl_->rootTrack_.begin(tracing::InternedPos{idx.id}, tracing::InternedName{sym.id}, ts);
}
FastFunctionTracer::Guard::~Guard()
{
tracer_.impl_->rootTrack_.end(tracing::Timestamp::now());
}
auto EvalStateRetriever::getName(tracing::InternedName name) const -> std::string
{
if (name.key == 0) {
return "";
}
// XXX: This reaches into the internals of SymbolTable in order to be able
// to do its work while keeping libtracing decoupled from the precise
// implementation of Nix
return evalState_.symbols[Symbol(name.key)];
}
auto EvalStateRetriever::getPos(tracing::InternedPos idx, std::string const funcName) const
-> tracing::TracePos
{
// XXX: This reaches into the internals of PosTable in order to be able
// to do its work while keeping libtracing decoupled from the precise
// implementation of Nix
//
// TODO: figure out a better way, maybe just giving it an opaque reference
// ideally without allocating
auto pos = evalState_.positions[PosIdx(idx.key)];
std::string out;
auto fileName = std::visit(
overloaded{
[](const std::monostate &) { return "«none»"s; },
[](const Pos::Stdin &) { return "«stdin»"s; },
[](const Pos::String & s) { return "«string»"s; },
[](const SourcePath & path) { return path.to_string(); }
},
pos.origin
);
return tracing::TracePos{
.fileName = fileName,
.functionName = funcName,
.lineNumber = pos.line,
};
}
}

View file

@ -0,0 +1,42 @@
#pragma once
/**
* @file
* @brief This is a fast function tracer based on libtracing. It integrates
* with Nix's interning infrastructure to produce relatively reasonably
* processable traces compared to the existing function-trace.
*/
#include "types.hh"
#include "trace-writer.hh"
#include <memory>
namespace nix {
class Symbol;
class PosIdx;
class EvalState;
class FastFunctionTracer {
class Impl;
std::unique_ptr<Impl> impl_;
public:
/** FIXME(threading): this is not thread safe */
class Guard {
FastFunctionTracer &tracer_;
explicit Guard(FastFunctionTracer &tracer, tracing::Timestamp ts, const Symbol sym, const PosIdx idx);
friend class FastFunctionTracer;
public:
~Guard();
};
explicit FastFunctionTracer(EvalState &evalState, Path outputFile);
/** Get a guard that on destruction ends a trace-level scope (i.e. function call) */
Guard enterFunction(const Symbol sym, const PosIdx pos, tracing::Timestamp ts = tracing::Timestamp::now());
~FastFunctionTracer();
};
}

View file

@ -71,6 +71,7 @@ libexpr_sources = files(
'eval-error.cc',
'eval-settings.cc',
'eval.cc',
'fast-function-tracer.cc',
'function-trace.cc',
'get-drvs.cc',
'json-to-value.cc',
@ -102,6 +103,7 @@ libexpr_headers = files(
'eval-inline.hh',
'eval-settings.hh',
'eval.hh',
'fast-function-tracer.cc',
'flake/flake.hh',
'flake/flakeref.hh',
'flake/lockfile.hh',
@ -133,6 +135,7 @@ libexpr = library(
lexer_tab,
libexpr_generated_headers,
dependencies : [
liblixtracing,
liblixutil,
liblixstore,
liblixfetchers,

View file

@ -10,6 +10,10 @@ class PosIdx
friend struct LazyPosAcessors;
friend class PosTable;
// FastFunctionTracer internals
friend class EvalStateRetriever;
friend class FastFunctionTracer;
private:
uint32_t id;

View file

@ -52,6 +52,10 @@ class Symbol
{
friend class SymbolTable;
// FastFunctionTracer internals
friend class EvalStateRetriever;
friend class FastFunctionTracer;
private:
uint32_t id;

View file

@ -1,6 +1,9 @@
#include "environment-variables.hh"
#include "loggers.hh"
#include "logging/chain-logger.hh"
#include "logging/trace-logger.hh"
#include "progress-bar.hh"
#include <memory>
namespace nix {
@ -20,23 +23,35 @@ LogFormat parseLogFormat(const std::string & logFormatStr) {
throw Error("option 'log-format' has an invalid value '%s'", logFormatStr);
}
Logger * makeDefaultLogger() {
switch (defaultLogFormat) {
case LogFormat::raw:
return makeSimpleLogger(false);
case LogFormat::rawWithLogs:
return makeSimpleLogger(true);
case LogFormat::internalJSON:
return makeJSONLogger(*makeSimpleLogger(true));
case LogFormat::bar:
return makeProgressBar();
case LogFormat::barWithLogs: {
auto logger = makeProgressBar();
logger->setPrintBuildLogs(true);
return logger;
}
default:
abort();
std::shared_ptr<Logger> makeDefaultLogger() {
auto pickMainLogger = []() {
switch (defaultLogFormat) {
case LogFormat::raw:
return makeSimpleLogger(false);
case LogFormat::rawWithLogs:
return makeSimpleLogger(true);
case LogFormat::internalJSON:
return makeJSONLogger(*makeSimpleLogger(true));
case LogFormat::bar:
return makeProgressBar();
case LogFormat::barWithLogs: {
auto logger = makeProgressBar();
logger->setPrintBuildLogs(true);
return logger;
}
default:
abort();
}
};
auto mainLogger = pickMainLogger();
// TODO: this is absolutely not the way to configure this
auto traceFile = getEnv("NIX_TRACE_FILE");
if (traceFile.has_value()) {
return std::shared_ptr<Logger>(std::make_shared<ChainLogger>(std::vector{mainLogger, std::shared_ptr<Logger>(std::make_shared<TraceLogger>(*traceFile))}));
} else {
return mainLogger;
}
}

View file

@ -1,10 +1,10 @@
#include "progress-bar.hh"
#include "file-system.hh"
#include "strings.hh"
#include "sync.hh"
#include "store-api.hh"
#include "names.hh"
#include "terminal.hh"
#include <atomic>
#include <map>
#include <thread>
#include <sstream>
@ -119,7 +119,14 @@ public:
{
{
auto state(state_.lock());
if (!state->active) return;
if (!state->active) {
// Even if the thread is inactive, the handle needs to be
// explicitly joined to not call terminate if it is destructed.
if (updateThread.joinable()) {
updateThread.join();
}
return;
}
state->active = false;
writeToStderr("\r\e[K");
updateCV.notify_one();
@ -531,9 +538,9 @@ public:
}
};
Logger * makeProgressBar()
std::shared_ptr<Logger> makeProgressBar()
{
return new ProgressBar(shouldANSI());
return std::make_shared<ProgressBar>(shouldANSI());
}
void startProgressBar()
@ -543,7 +550,7 @@ void startProgressBar()
void stopProgressBar()
{
auto progressBar = dynamic_cast<ProgressBar *>(logger);
auto progressBar = dynamic_cast<ProgressBar *>(logger.get());
if (progressBar) progressBar->stop();
}

View file

@ -5,7 +5,7 @@
namespace nix {
Logger * makeProgressBar();
std::shared_ptr<Logger> makeProgressBar();
void startProgressBar();

View file

@ -263,7 +263,7 @@ struct ClientSettings
}
};
static void performOp(TunnelLogger * logger, ref<Store> store,
static void performOp(std::shared_ptr<TunnelLogger> logger, ref<Store> store,
TrustedFlag trusted, RecursiveFlag recursive, WorkerProto::Version clientVersion,
Source & from, BufferedSink & to, WorkerProto::Op op)
{
@ -1014,7 +1014,7 @@ void processConnection(
if (clientVersion < 0x10a)
throw Error("the Nix client version is too old");
auto tunnelLogger = new TunnelLogger(to, clientVersion);
auto tunnelLogger = std::make_shared<TunnelLogger>(to, clientVersion);
auto prevLogger = nix::logger;
// FIXME
if (!recursive)

45
src/libtracing/main.cc Normal file
View file

@ -0,0 +1,45 @@
#include "trace-writer.hh"
#include <fstream>
#include <iostream>
using namespace nix::tracing;
class MyInternedRetriever : public InternedRetriever
{
virtual auto getName(InternedName idx) const -> std::string override
{
return "meow";
}
virtual auto getPos(InternedPos idx, std::string const funcName) const -> TracePos override
{
return TracePos{
.fileName = "file.nix",
.functionName = "fun",
.lineNumber = 12,
};
}
public:
virtual ~MyInternedRetriever() = default;
};
int main(int argc, char **argv)
{
if (argc < 1) {
std::cerr << "provide filename pls\n";
return 1;
}
nix::tracing::TraceWriter tw{std::make_unique<MyInternedRetriever>()};
auto track = tw.trackByTid(1, 2, "track");
track.begin(InternedPos{1}, InternedName{3}, Timestamp{250});
track.end(Timestamp{1235});
std::ofstream ofs(argv[1]);
auto buf = tw.splat();
ofs.write(reinterpret_cast<const char *>(buf.data()), buf.size());
return 0;
}

View file

@ -0,0 +1 @@
// Intentionally empty (crbug.com/998165)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,47 @@
libtracing_sources = files(
'trace-writer.cc',
'protozero_src/field.cc',
'protozero_src/gen_field_helpers.cc',
'protozero_src/message_arena.cc',
'protozero_src/message.cc',
'protozero_src/packed_repeated_fields.cc',
'protozero_src/proto_decoder.cc',
'protozero_src/scattered_heap_buffer.cc',
'protozero_src/scattered_stream_null_delegate.cc',
'protozero_src/scattered_stream_writer.cc',
'protozero_src/static_buffer.cc',
'protozero_src/virtual_destructors.cc',
)
subdir('perfetto/protozero')
libtracing_headers = [
protozero_headers,
files(
'trace-writer.hh',
),
]
libtracing = library(
'lixtracing',
libtracing_sources,
dependencies : [ ],
install : true,
# FIXME(Qyriad): is this right?
install_rpath : libdir,
cpp_args : [ '-DPERFETTO_DISABLE_LOG' ],
include_directories : include_directories('./perfetto/base/build_configs/bazel')
)
liblixtracing = declare_dependency(
include_directories : include_directories('.'),
link_with : libtracing,
)
tracing_test = executable(
'lixtracingtest',
files('main.cc'),
dependencies : [ liblixtracing ],
install : false,
)

View file

@ -0,0 +1,33 @@
# Copyright (C) 2017 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.
import("../../../gn/perfetto.gni")
source_set("base") {
sources = [
"build_config.h",
"compiler.h",
"export.h",
"flat_set.h",
"logging.h",
"platform_handle.h",
"proc_utils.h",
"status.h",
"task_runner.h",
"template_util.h",
"thread_utils.h",
"time.h",
]
public_deps = [ "../public:base" ]
}

View file

@ -0,0 +1,160 @@
/*
* Copyright (C) 2017 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 INCLUDE_PERFETTO_BASE_BUILD_CONFIG_H_
#define INCLUDE_PERFETTO_BASE_BUILD_CONFIG_H_
// Allows to define build flags that give a compiler error if the header that
// defined the flag is not included, instead of silently ignoring the #if block.
#define PERFETTO_BUILDFLAG_CAT_INDIRECT(a, b) a##b
#define PERFETTO_BUILDFLAG_CAT(a, b) PERFETTO_BUILDFLAG_CAT_INDIRECT(a, b)
#define PERFETTO_BUILDFLAG(flag) \
(PERFETTO_BUILDFLAG_CAT(PERFETTO_BUILDFLAG_DEFINE_, flag)())
#if defined(__ANDROID__)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_ANDROID() 1
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_LINUX() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_WIN() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_APPLE() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_MAC() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_IOS() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_WASM() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_FUCHSIA() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_NACL() 0
#elif defined(__APPLE__)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_ANDROID() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_APPLE() 1
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_LINUX() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_WIN() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_WASM() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_FUCHSIA() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_NACL() 0
// Include TARGET_OS_IPHONE when on __APPLE__ systems.
#include <TargetConditionals.h>
#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_MAC() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_IOS() 1
#else
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_MAC() 1
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_IOS() 0
#endif
#elif defined(__linux__)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_ANDROID() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_LINUX() 1
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_WIN() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_APPLE() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_MAC() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_IOS() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_WASM() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_FUCHSIA() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_NACL() 0
#elif defined(_WIN32)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_ANDROID() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_LINUX() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_WIN() 1
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_APPLE() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_MAC() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_IOS() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_WASM() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_FUCHSIA() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_NACL() 0
#elif defined(__EMSCRIPTEN__)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_ANDROID() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_LINUX() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_WIN() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_APPLE() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_MAC() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_IOS() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_WASM() 1
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_FUCHSIA() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_NACL() 0
#elif defined(__Fuchsia__)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_ANDROID() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_APPLE() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_MAC() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_IOS() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_LINUX() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_WIN() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_WASM() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_FUCHSIA() 1
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_NACL() 0
#elif defined(__native_client__)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_ANDROID() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_LINUX() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_WIN() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_APPLE() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_MAC() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_IOS() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_WASM() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_FUCHSIA() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_NACL() 1
#else
#error OS not supported (see build_config.h)
#endif
#if defined(__clang__)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_COMPILER_CLANG() 1
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_COMPILER_GCC() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_COMPILER_MSVC() 0
#elif defined(__GNUC__) // Careful: Clang also defines this!
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_COMPILER_CLANG() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_COMPILER_GCC() 1
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_COMPILER_MSVC() 0
#elif defined(_MSC_VER)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_COMPILER_CLANG() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_COMPILER_GCC() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_COMPILER_MSVC() 1
#else
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_COMPILER_CLANG() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_COMPILER_GCC() 0
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_COMPILER_MSVC() 0
#endif
#if defined(PERFETTO_BUILD_WITH_ANDROID_USERDEBUG)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_ANDROID_USERDEBUG_BUILD() 1
#else
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_ANDROID_USERDEBUG_BUILD() 0
#endif
// Processor architecture detection. For more info on what's defined, see:
// http://msdn.microsoft.com/en-us/library/b0084kay.aspx
// http://www.agner.org/optimize/calling_conventions.pdf
// or with gcc, run: "echo | gcc -E -dM -"
#if defined(__aarch64__) || defined(_M_ARM64)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_ARCH_CPU_ARM64() 1
#else
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_ARCH_CPU_ARM64() 0
#endif
#if defined(__x86_64__) || defined(_M_X64)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_ARCH_CPU_X86_64() 1
#else
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_ARCH_CPU_X86_64() 0
#endif
// perfetto_build_flags.h contains the tweakable build flags defined via GN.
// - In GN builds (e.g., standalone, chromium, v8) this file is generated at
// build time via the gen_rule //gn/gen_buildflags.
// - In Android in-tree builds, this file is generated by tools/gen_android_bp
// and checked in into include/perfetto/base/build_configs/android_tree/. The
// default cflags add this path to the default include path.
// - Similarly, in bazel builds, this file is generated by tools/gen_bazel and
// checked in into include/perfetto/base/build_configs/bazel/.
// - In amalgamated builds, this file is generated by tools/gen_amalgamated and
// added to the amalgamated headers.
#include "perfetto_build_flags.h" // no-include-violation-check
#endif // INCLUDE_PERFETTO_BASE_BUILD_CONFIG_H_

View file

@ -0,0 +1,51 @@
/*
* Copyright (C) 2019 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.
*/
// Generated by write_buildflag_header.py
// fix_include_guards: off
#ifndef GEN_BUILD_CONFIG_PERFETTO_BUILD_FLAGS_H_
#define GEN_BUILD_CONFIG_PERFETTO_BUILD_FLAGS_H_
// clang-format off
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_ANDROID_BUILD() (1)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_CHROMIUM_BUILD() (0)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_STANDALONE_BUILD() (0)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_START_DAEMONS() (0)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_IPC() (1)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_WATCHDOG() (PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_ANDROID() || PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_LINUX())
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_COMPONENT_BUILD() (0)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_FORCE_DLOG_ON() (0)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_FORCE_DLOG_OFF() (0)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_FORCE_DCHECK_ON() (0)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_FORCE_DCHECK_OFF() (0)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_VERBOSE_LOGS() (1)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_VERSION_GEN() (1)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_TP_PERCENTILE() (1)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_TP_LINENOISE() (0)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_TP_HTTPD() (1)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_TP_JSON() (0)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_LOCAL_SYMBOLIZER() (PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_LINUX() || PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_MAC() ||PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_WIN())
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_ZLIB() (1)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_TRACED_PERF() (1)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_HEAPPROFD() (1)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_STDERR_CRASH_DUMP() (0)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_X64_CPU_OPT() (0)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_LLVM_DEMANGLE() (0)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_SYSTEM_CONSUMER() (1)
// clang-format on
#endif // GEN_BUILD_CONFIG_PERFETTO_BUILD_FLAGS_H_

View file

@ -0,0 +1,51 @@
/*
* Copyright (C) 2019 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.
*/
// Generated by write_buildflag_header.py
// fix_include_guards: off
#ifndef GEN_BUILD_CONFIG_PERFETTO_BUILD_FLAGS_H_
#define GEN_BUILD_CONFIG_PERFETTO_BUILD_FLAGS_H_
// clang-format off
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_ANDROID_BUILD() (0)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_CHROMIUM_BUILD() (0)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_STANDALONE_BUILD() (1)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_START_DAEMONS() (1)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_IPC() (1)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_WATCHDOG() (0)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_COMPONENT_BUILD() (0)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_FORCE_DLOG_ON() (0)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_FORCE_DLOG_OFF() (0)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_FORCE_DCHECK_ON() (0)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_FORCE_DCHECK_OFF() (1)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_VERBOSE_LOGS() (1)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_VERSION_GEN() (1)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_TP_PERCENTILE() (1)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_TP_LINENOISE() (1)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_TP_HTTPD() (1)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_TP_JSON() (1)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_LOCAL_SYMBOLIZER() (PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_LINUX() || PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_MAC() ||PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_WIN())
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_ZLIB() (1)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_TRACED_PERF() (0)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_HEAPPROFD() (0)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_STDERR_CRASH_DUMP() (0)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_X64_CPU_OPT() (0)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_LLVM_DEMANGLE() (1)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_SYSTEM_CONSUMER() (1)
// clang-format on
#endif // GEN_BUILD_CONFIG_PERFETTO_BUILD_FLAGS_H_

View file

@ -0,0 +1,157 @@
/*
* Copyright (C) 2019 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 INCLUDE_PERFETTO_BASE_COMPILER_H_
#define INCLUDE_PERFETTO_BASE_COMPILER_H_
#include <stddef.h>
#include <type_traits>
#include "perfetto/public/compiler.h"
// __has_attribute is supported only by clang and recent versions of GCC.
// Add a layer to wrap the __has_attribute macro.
#if defined(__has_attribute)
#define PERFETTO_HAS_ATTRIBUTE(x) __has_attribute(x)
#else
#define PERFETTO_HAS_ATTRIBUTE(x) 0
#endif
#if defined(__GNUC__) || defined(__clang__)
#define PERFETTO_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
#else
#define PERFETTO_WARN_UNUSED_RESULT
#endif
#if defined(__GNUC__) || defined(__clang__)
#define PERFETTO_UNUSED __attribute__((unused))
#else
#define PERFETTO_UNUSED
#endif
#if defined(__clang__)
#define PERFETTO_ALWAYS_INLINE __attribute__((__always_inline__))
#define PERFETTO_NO_INLINE __attribute__((__noinline__))
#else
// GCC is too pedantic and often fails with the error:
// "always_inline function might not be inlinable"
#define PERFETTO_ALWAYS_INLINE
#define PERFETTO_NO_INLINE
#endif
#if defined(__GNUC__) || defined(__clang__)
#define PERFETTO_NORETURN __attribute__((__noreturn__))
#else
#define PERFETTO_NORETURN __declspec(noreturn)
#endif
#if defined(__GNUC__) || defined(__clang__)
#define PERFETTO_DEBUG_FUNCTION_IDENTIFIER() __PRETTY_FUNCTION__
#elif defined(_MSC_VER)
#define PERFETTO_DEBUG_FUNCTION_IDENTIFIER() __FUNCSIG__
#else
#define PERFETTO_DEBUG_FUNCTION_IDENTIFIER() \
static_assert(false, "Not implemented for this compiler")
#endif
#if defined(__GNUC__) || defined(__clang__)
#define PERFETTO_PRINTF_FORMAT(x, y) \
__attribute__((__format__(__printf__, x, y)))
#else
#define PERFETTO_PRINTF_FORMAT(x, y)
#endif
#if defined(__GNUC__) || defined(__clang__)
#define PERFETTO_POPCOUNT(x) __builtin_popcountll(x)
#else
#include <intrin.h>
#define PERFETTO_POPCOUNT(x) __popcnt64(x)
#endif
#if defined(__clang__)
#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
extern "C" void __asan_poison_memory_region(void const volatile*, size_t);
extern "C" void __asan_unpoison_memory_region(void const volatile*, size_t);
#define PERFETTO_ASAN_POISON(a, s) __asan_poison_memory_region((a), (s))
#define PERFETTO_ASAN_UNPOISON(a, s) __asan_unpoison_memory_region((a), (s))
#else
#define PERFETTO_ASAN_POISON(addr, size)
#define PERFETTO_ASAN_UNPOISON(addr, size)
#endif // __has_feature(address_sanitizer)
#else
#define PERFETTO_ASAN_POISON(addr, size)
#define PERFETTO_ASAN_UNPOISON(addr, size)
#endif // __clang__
#if defined(__GNUC__) || defined(__clang__)
#define PERFETTO_IS_LITTLE_ENDIAN() __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#else
// Assume all MSVC targets are little endian.
#define PERFETTO_IS_LITTLE_ENDIAN() 1
#endif
// This is used for exporting xxxMain() symbols (e.g., PerfettoCmdMain,
// ProbesMain) from libperfetto.so when the GN arg monolithic_binaries = false.
#if defined(__GNUC__) || defined(__clang__)
#define PERFETTO_EXPORT_ENTRYPOINT __attribute__((visibility("default")))
#else
// TODO(primiano): on Windows this should be a pair of dllexport/dllimport. But
// that requires a -DXXX_IMPLEMENTATION depending on whether we are on the
// impl-site or call-site. Right now it's not worth the trouble as we
// force-export the xxxMain() symbols only on Android, where we pack all the
// code for N binaries into one .so to save binary size. On Windows we support
// only monolithic binaries, as they are easier to deal with.
#define PERFETTO_EXPORT_ENTRYPOINT
#endif
// Disables thread safety analysis for functions where the compiler can't
// accurate figure out which locks are being held.
#if defined(__clang__)
#define PERFETTO_NO_THREAD_SAFETY_ANALYSIS \
__attribute__((no_thread_safety_analysis))
#else
#define PERFETTO_NO_THREAD_SAFETY_ANALYSIS
#endif
// Disables undefined behavior analysis for a function.
#if defined(__clang__)
#define PERFETTO_NO_SANITIZE_UNDEFINED __attribute__((no_sanitize("undefined")))
#else
#define PERFETTO_NO_SANITIZE_UNDEFINED
#endif
// Avoid calling the exit-time destructor on an object with static lifetime.
#if PERFETTO_HAS_ATTRIBUTE(no_destroy)
#define PERFETTO_HAS_NO_DESTROY() 1
#define PERFETTO_NO_DESTROY __attribute__((no_destroy))
#else
#define PERFETTO_HAS_NO_DESTROY() 0
#define PERFETTO_NO_DESTROY
#endif
// Macro for telling -Wimplicit-fallthrough that a fallthrough is intentional.
#define PERFETTO_FALLTHROUGH [[fallthrough]]
namespace perfetto {
namespace base {
template <typename... T>
inline void ignore_result(const T&...) {}
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_BASE_COMPILER_H_

View file

@ -0,0 +1,41 @@
/*
* Copyright (C) 2018 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 INCLUDE_PERFETTO_BASE_EXPORT_H_
#define INCLUDE_PERFETTO_BASE_EXPORT_H_
#include "perfetto/base/build_config.h"
#include "perfetto/public/abi/export.h"
// PERFETTO_EXPORT_COMPONENT: Exports a symbol among C++ components when
// building with is_component = true (mostly used by chromium build).
#if PERFETTO_BUILDFLAG(PERFETTO_COMPONENT_BUILD)
#if defined(PERFETTO_IMPLEMENTATION)
#define PERFETTO_EXPORT_COMPONENT PERFETTO_INTERNAL_DLL_EXPORT
#else
#define PERFETTO_EXPORT_COMPONENT PERFETTO_INTERNAL_DLL_IMPORT
#endif
#else // !PERFETTO_BUILDFLAG(PERFETTO_COMPONENT_BUILD)
#if !defined(PERFETTO_EXPORT_COMPONENT)
#define PERFETTO_EXPORT_COMPONENT
#endif // !defined(PERFETTO_EXPORT_COMPONENT)
#endif // PERFETTO_BUILDFLAG(PERFETTO_COMPONENT_BUILD)
#endif // INCLUDE_PERFETTO_BASE_EXPORT_H_

View file

@ -0,0 +1,100 @@
/*
* Copyright (C) 2019 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 INCLUDE_PERFETTO_BASE_FLAT_SET_H_
#define INCLUDE_PERFETTO_BASE_FLAT_SET_H_
#include <algorithm>
#include <vector>
// A vector-based set::set-like container.
// It's more cache friendly than std::*set and performs for cases where:
// 1. A high number of dupes is expected (e.g. pid tracking in ftrace).
// 2. The working set is small (hundreds of elements).
// Performance characteristics (for uniformly random insertion order):
// - For smaller insertions (up to ~500), it outperforms both std::set<int> and
// std::unordered_set<int> by ~3x.
// - Up until 4k insertions, it is always faster than std::set<int>.
// - unordered_set<int> is faster with more than 2k insertions.
// - unordered_set, however, it's less memory efficient and has more caveats
// (see chromium's base/containers/README.md).
//
// See flat_set_benchmark.cc and the charts in go/perfetto-int-set-benchmark.
namespace perfetto {
namespace base {
template <typename T>
class FlatSet {
public:
using value_type = T;
using const_pointer = const T*;
using iterator = typename std::vector<T>::iterator;
using const_iterator = typename std::vector<T>::const_iterator;
FlatSet() = default;
// Mainly for tests. Deliberately not marked as "explicit".
FlatSet(std::initializer_list<T> initial) : entries_(initial) {
std::sort(entries_.begin(), entries_.end());
entries_.erase(std::unique(entries_.begin(), entries_.end()),
entries_.end());
}
const_iterator find(T value) const {
auto entries_end = entries_.end();
auto it = std::lower_bound(entries_.begin(), entries_end, value);
return (it != entries_end && *it == value) ? it : entries_end;
}
size_t count(T value) const { return find(value) == entries_.end() ? 0 : 1; }
std::pair<iterator, bool> insert(T value) {
auto entries_end = entries_.end();
auto it = std::lower_bound(entries_.begin(), entries_end, value);
if (it != entries_end && *it == value)
return std::make_pair(it, false);
// If the value is not found |it| is either end() or the next item strictly
// greater than |value|. In both cases we want to insert just before that.
it = entries_.insert(it, std::move(value));
return std::make_pair(it, true);
}
size_t erase(T value) {
auto it = find(value);
if (it == entries_.end())
return 0;
entries_.erase(it);
return 1;
}
void clear() { entries_.clear(); }
bool empty() const { return entries_.empty(); }
void reserve(size_t n) { entries_.reserve(n); }
size_t size() const { return entries_.size(); }
const_iterator begin() const { return entries_.begin(); }
const_iterator end() const { return entries_.end(); }
private:
std::vector<T> entries_;
};
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_BASE_FLAT_SET_H_

View file

@ -0,0 +1,248 @@
/*
* Copyright (C) 2017 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 INCLUDE_PERFETTO_BASE_LOGGING_H_
#define INCLUDE_PERFETTO_BASE_LOGGING_H_
#include <errno.h>
#include <string.h> // For strerror.
#include "perfetto/base/build_config.h"
#include "perfetto/base/compiler.h"
#include "perfetto/base/export.h"
#if defined(__GNUC__) || defined(__clang__)
// Ignore GCC warning about a missing argument for a variadic macro parameter.
#pragma GCC system_header
#endif
#if PERFETTO_BUILDFLAG(PERFETTO_FORCE_DCHECK_ON)
#define PERFETTO_DCHECK_IS_ON() 1
#elif PERFETTO_BUILDFLAG(PERFETTO_FORCE_DCHECK_OFF)
#define PERFETTO_DCHECK_IS_ON() 0
#elif defined(DCHECK_ALWAYS_ON) || \
(!defined(NDEBUG) && (PERFETTO_BUILDFLAG(PERFETTO_STANDALONE_BUILD) || \
PERFETTO_BUILDFLAG(PERFETTO_CHROMIUM_BUILD) || \
PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)))
#define PERFETTO_DCHECK_IS_ON() 1
#else
#define PERFETTO_DCHECK_IS_ON() 0
#endif
#if PERFETTO_BUILDFLAG(PERFETTO_FORCE_DLOG_ON)
#define PERFETTO_DLOG_IS_ON() 1
#elif PERFETTO_BUILDFLAG(PERFETTO_FORCE_DLOG_OFF)
#define PERFETTO_DLOG_IS_ON() 0
#else
#define PERFETTO_DLOG_IS_ON() PERFETTO_DCHECK_IS_ON()
#endif
#if defined(PERFETTO_ANDROID_ASYNC_SAFE_LOG)
#if !PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
!PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
#error "Async-safe logging is limited to Android tree builds"
#endif
// For binaries which need a very lightweight logging implementation.
// Note that this header is incompatible with android/log.h.
#include <async_safe/log.h>
#elif PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
// Normal android logging.
#include <android/log.h>
#endif
// Enable the "Print the most recent PERFETTO_LOG(s) before crashing" feature
// on Android in-tree builds and on standalone builds (mainly for testing).
// This is deliberately no PERFETTO_OS_ANDROID because we don't want this
// feature when perfetto is embedded in other Android projects (e.g. SDK).
// TODO(b/203795298): TFLite is using the client library in blaze builds and is
// targeting API 19. For now disable the feature based on API level.
#if defined(PERFETTO_ANDROID_ASYNC_SAFE_LOG)
#define PERFETTO_ENABLE_LOG_RING_BUFFER() 0
#elif PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
#define PERFETTO_ENABLE_LOG_RING_BUFFER() 1
#elif PERFETTO_BUILDFLAG(PERFETTO_STANDALONE_BUILD) && \
(!PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
(defined(__ANDROID_API__) && __ANDROID_API__ >= 21))
#define PERFETTO_ENABLE_LOG_RING_BUFFER() 0
#else
#define PERFETTO_ENABLE_LOG_RING_BUFFER() 0
#endif
namespace perfetto {
namespace base {
// Constexpr functions to extract basename(__FILE__), e.g.: ../foo/f.c -> f.c .
constexpr const char* StrEnd(const char* s) {
return *s ? StrEnd(s + 1) : s;
}
constexpr const char* BasenameRecursive(const char* s,
const char* begin,
const char* end) {
return (*s == '/' && s < end)
? (s + 1)
: ((s > begin) ? BasenameRecursive(s - 1, begin, end) : s);
}
constexpr const char* Basename(const char* str) {
return BasenameRecursive(StrEnd(str), str, StrEnd(str));
}
enum LogLev { kLogDebug = 0, kLogInfo, kLogImportant, kLogError };
struct LogMessageCallbackArgs {
LogLev level;
int line;
const char* filename;
const char* message;
};
using LogMessageCallback = void (*)(LogMessageCallbackArgs);
// This is not thread safe and must be called before using tracing from other
// threads.
PERFETTO_EXPORT_COMPONENT void SetLogMessageCallback(
LogMessageCallback callback);
PERFETTO_EXPORT_COMPONENT void LogMessage(LogLev,
const char* fname,
int line,
const char* fmt,
...) PERFETTO_PRINTF_FORMAT(4, 5);
// This is defined in debug_crash_stack_trace.cc, but that is only linked in
// standalone && debug builds, see enable_perfetto_stderr_crash_dump in
// perfetto.gni.
PERFETTO_EXPORT_COMPONENT void EnableStacktraceOnCrashForDebug();
#if PERFETTO_ENABLE_LOG_RING_BUFFER()
// Gets a snapshot of the logs from the internal log ring buffer and:
// - On Android in-tree builds: Passes that to android_set_abort_message().
// That will attach the logs to the crash report.
// - On standalone builds (all otther OSes) prints that on stderr.
// This function must called only once, right before inducing a crash (This is
// because android_set_abort_message() can only be called once).
PERFETTO_EXPORT_COMPONENT void MaybeSerializeLastLogsForCrashReporting();
#else
inline void MaybeSerializeLastLogsForCrashReporting() {}
#endif
#if defined(PERFETTO_ANDROID_ASYNC_SAFE_LOG)
#define PERFETTO_XLOG(level, fmt, ...) \
do { \
async_safe_format_log((ANDROID_LOG_DEBUG + level), "perfetto", \
"%s:%d " fmt, ::perfetto::base::Basename(__FILE__), \
__LINE__, ##__VA_ARGS__); \
} while (0)
#elif defined(PERFETTO_DISABLE_LOG)
#define PERFETTO_XLOG(level, fmt, ...) ::perfetto::base::ignore_result(level, \
fmt, ##__VA_ARGS__)
#else
#define PERFETTO_XLOG(level, fmt, ...) \
::perfetto::base::LogMessage(level, ::perfetto::base::Basename(__FILE__), \
__LINE__, fmt, ##__VA_ARGS__)
#endif
#if defined(_MSC_VER)
#define PERFETTO_IMMEDIATE_CRASH() \
do { \
::perfetto::base::MaybeSerializeLastLogsForCrashReporting(); \
__debugbreak(); \
__assume(0); \
} while (0)
#else
#define PERFETTO_IMMEDIATE_CRASH() \
do { \
::perfetto::base::MaybeSerializeLastLogsForCrashReporting(); \
__builtin_trap(); \
__builtin_unreachable(); \
} while (0)
#endif
#if PERFETTO_BUILDFLAG(PERFETTO_VERBOSE_LOGS)
#define PERFETTO_LOG(fmt, ...) \
PERFETTO_XLOG(::perfetto::base::kLogInfo, fmt, ##__VA_ARGS__)
#else // PERFETTO_BUILDFLAG(PERFETTO_VERBOSE_LOGS)
#define PERFETTO_LOG(...) ::perfetto::base::ignore_result(__VA_ARGS__)
#endif // PERFETTO_BUILDFLAG(PERFETTO_VERBOSE_LOGS)
#define PERFETTO_ILOG(fmt, ...) \
PERFETTO_XLOG(::perfetto::base::kLogImportant, fmt, ##__VA_ARGS__)
#define PERFETTO_ELOG(fmt, ...) \
PERFETTO_XLOG(::perfetto::base::kLogError, fmt, ##__VA_ARGS__)
#define PERFETTO_FATAL(fmt, ...) \
do { \
PERFETTO_PLOG(fmt, ##__VA_ARGS__); \
PERFETTO_IMMEDIATE_CRASH(); \
} while (0)
#if defined(__GNUC__) || defined(__clang__)
#define PERFETTO_PLOG(x, ...) \
PERFETTO_ELOG(x " (errno: %d, %s)", ##__VA_ARGS__, errno, strerror(errno))
#else
// MSVC expands __VA_ARGS__ in a different order. Give up, not worth it.
#define PERFETTO_PLOG PERFETTO_ELOG
#endif
#define PERFETTO_CHECK(x) \
do { \
if (PERFETTO_UNLIKELY(!(x))) { \
PERFETTO_PLOG("%s", "PERFETTO_CHECK(" #x ")"); \
PERFETTO_IMMEDIATE_CRASH(); \
} \
} while (0)
#if PERFETTO_DLOG_IS_ON()
#define PERFETTO_DLOG(fmt, ...) \
PERFETTO_XLOG(::perfetto::base::kLogDebug, fmt, ##__VA_ARGS__)
#if defined(__GNUC__) || defined(__clang__)
#define PERFETTO_DPLOG(x, ...) \
PERFETTO_DLOG(x " (errno: %d, %s)", ##__VA_ARGS__, errno, strerror(errno))
#else
// MSVC expands __VA_ARGS__ in a different order. Give up, not worth it.
#define PERFETTO_DPLOG PERFETTO_DLOG
#endif
#else // PERFETTO_DLOG_IS_ON()
#define PERFETTO_DLOG(...) ::perfetto::base::ignore_result(__VA_ARGS__)
#define PERFETTO_DPLOG(...) ::perfetto::base::ignore_result(__VA_ARGS__)
#endif // PERFETTO_DLOG_IS_ON()
#if PERFETTO_DCHECK_IS_ON()
#define PERFETTO_DCHECK(x) PERFETTO_CHECK(x)
#define PERFETTO_DFATAL(...) PERFETTO_FATAL(__VA_ARGS__)
#define PERFETTO_DFATAL_OR_ELOG(...) PERFETTO_DFATAL(__VA_ARGS__)
#else // PERFETTO_DCHECK_IS_ON()
#define PERFETTO_DCHECK(x) \
do { \
} while (false && (x))
#define PERFETTO_DFATAL(...) ::perfetto::base::ignore_result(__VA_ARGS__)
#define PERFETTO_DFATAL_OR_ELOG(...) PERFETTO_ELOG(__VA_ARGS__)
#endif // PERFETTO_DCHECK_IS_ON()
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_BASE_LOGGING_H_

View file

@ -0,0 +1,71 @@
/*
* 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 INCLUDE_PERFETTO_BASE_PLATFORM_HANDLE_H_
#define INCLUDE_PERFETTO_BASE_PLATFORM_HANDLE_H_
#include <stdint.h>
#include "perfetto/base/build_config.h"
namespace perfetto {
namespace base {
// PlatformHandle should be used only for types that are HANDLE(s) in Windows.
// It should NOT be used to blanket-replace "int fd" in the codebase.
// Windows has two types of "handles", which, in UNIX-land, both map to int:
// 1. File handles returned by the posix-compatibility API like _open().
// These are just int(s) and should stay such, because all the posix-like API
// in Windows.h take an int, not a HANDLE.
// 2. Handles returned by old-school WINAPI like CreateFile, CreateEvent etc.
// These are proper HANDLE(s). PlatformHandle should be used here.
//
// On Windows, sockets have their own type (SOCKET) which is neither a HANDLE
// nor an int. However Windows SOCKET(s) can have an event HANDLE attached
// to them (which in Perfetto is a PlatformHandle), and that can be used in
// WaitForMultipleObjects, hence in base::TaskRunner.AddFileDescriptorWatch().
// On POSIX OSes, a SocketHandle is really just an int (a file descriptor).
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
// Windows.h typedefs HANDLE to void*, and SOCKET to uintptr_t. We use their
// types to avoid leaking Windows.h through our headers.
using PlatformHandle = void*;
using SocketHandle = uintptr_t;
// On Windows both nullptr and 0xffff... (INVALID_HANDLE_VALUE) are invalid.
struct PlatformHandleChecker {
static inline bool IsValid(PlatformHandle h) {
return h && h != reinterpret_cast<PlatformHandle>(-1);
}
};
#else
using PlatformHandle = int;
using SocketHandle = int;
struct PlatformHandleChecker {
static inline bool IsValid(PlatformHandle h) { return h >= 0; }
};
#endif
// The definition of this lives in base/file_utils.cc (to avoid creating an
// extra build edge for a one liner). This is really an alias for close() (UNIX)
// CloseHandle() (Windows). THe indirection layer is just to avoid leaking
// system headers like Windows.h through perfetto headers.
// Thre return value is always UNIX-style: 0 on success, -1 on failure.
int ClosePlatformHandle(PlatformHandle);
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_BASE_PLATFORM_HANDLE_H_

View file

@ -0,0 +1,59 @@
/*
* Copyright (C) 2019 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 INCLUDE_PERFETTO_BASE_PROC_UTILS_H_
#define INCLUDE_PERFETTO_BASE_PROC_UTILS_H_
#include <stdint.h>
#include "perfetto/base/build_config.h"
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
extern "C" {
// Prototype extracted from the Windows SDK to avoid including windows.h.
__declspec(dllimport) unsigned long __stdcall GetCurrentProcessId();
}
#elif PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
#include <zircon/process.h>
#include <zircon/types.h>
#else
#include <unistd.h>
#endif
namespace perfetto {
namespace base {
#if PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
using PlatformProcessId = zx_handle_t;
inline PlatformProcessId GetProcessId() {
return zx_process_self();
}
#elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
using PlatformProcessId = uint64_t;
inline PlatformProcessId GetProcessId() {
return static_cast<uint64_t>(GetCurrentProcessId());
}
#else
using PlatformProcessId = pid_t;
inline PlatformProcessId GetProcessId() {
return getpid();
}
#endif
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_BASE_PROC_UTILS_H_

View file

@ -0,0 +1,117 @@
/*
* Copyright (C) 2019 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 INCLUDE_PERFETTO_BASE_STATUS_H_
#define INCLUDE_PERFETTO_BASE_STATUS_H_
#include <optional>
#include <string>
#include <string_view>
#include <vector>
#include "perfetto/base/compiler.h"
#include "perfetto/base/export.h"
#include "perfetto/base/logging.h"
namespace perfetto {
namespace base {
// Represents either the success or the failure message of a function.
// This can used as the return type of functions which would usually return an
// bool for success or int for errno but also wants to add some string context
// (ususally for logging).
//
// Similar to absl::Status, an optional "payload" can also be included with more
// context about the error. This allows passing additional metadata about the
// error (e.g. location of errors, potential mitigations etc).
class PERFETTO_EXPORT_COMPONENT Status {
public:
Status() : ok_(true) {}
explicit Status(std::string msg) : ok_(false), message_(std::move(msg)) {
PERFETTO_CHECK(!message_.empty());
}
// Copy operations.
Status(const Status&) = default;
Status& operator=(const Status&) = default;
// Move operations. The moved-from state is valid but unspecified.
Status(Status&&) noexcept = default;
Status& operator=(Status&&) = default;
bool ok() const { return ok_; }
// When ok() is false this returns the error message. Returns the empty string
// otherwise.
const std::string& message() const { return message_; }
const char* c_message() const { return message_.c_str(); }
//////////////////////////////////////////////////////////////////////////////
// Payload Management APIs
//////////////////////////////////////////////////////////////////////////////
// Payloads can be attached to error statuses to provide additional context.
//
// Payloads are (key, value) pairs, where the key is a string acting as a
// unique "type URL" and the value is an opaque string. The "type URL" should
// be unique, follow the format of a URL and, ideally, documentation on how to
// interpret its associated data should be available.
//
// To attach a payload to a status object, call `Status::SetPayload()`.
// Similarly, to extract the payload from a status, call
// `Status::GetPayload()`.
//
// Note: the payload APIs are only meaningful to call when the status is an
// error. Otherwise, all methods are noops.
// Gets the payload for the given |type_url| if one exists.
//
// Will always return std::nullopt if |ok()|.
std::optional<std::string_view> GetPayload(std::string_view type_url) const;
// Sets the payload for the given key. The key should
//
// Will always do nothing if |ok()|.
void SetPayload(std::string_view type_url, std::string value);
// Erases the payload for the given string and returns true if the payload
// existed and was erased.
//
// Will always do nothing if |ok()|.
bool ErasePayload(std::string_view type_url);
private:
struct Payload {
std::string type_url;
std::string payload;
};
bool ok_ = false;
std::string message_;
std::vector<Payload> payloads_;
};
// Returns a status object which represents the Ok status.
inline Status OkStatus() {
return Status();
}
PERFETTO_PRINTF_FORMAT(1, 2) Status ErrStatus(const char* format, ...);
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_BASE_STATUS_H_

View file

@ -0,0 +1,78 @@
/*
* Copyright (C) 2017 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 INCLUDE_PERFETTO_BASE_TASK_RUNNER_H_
#define INCLUDE_PERFETTO_BASE_TASK_RUNNER_H_
#include <stdint.h>
#include <functional>
#include "perfetto/base/export.h"
#include "perfetto/base/platform_handle.h"
namespace perfetto {
namespace base {
// A generic interface to allow the library clients to interleave the execution
// of the tracing internals in their runtime environment.
// The expectation is that all tasks, which are queued either via PostTask() or
// AddFileDescriptorWatch(), are executed on the same sequence (either on the
// same thread, or on a thread pool that gives sequencing guarantees).
//
// Tasks are never executed synchronously inside PostTask and there is a full
// memory barrier between tasks.
//
// All methods of this interface can be called from any thread.
class PERFETTO_EXPORT_COMPONENT TaskRunner {
public:
virtual ~TaskRunner();
// Schedule a task for immediate execution. Immediate tasks are always
// executed in the order they are posted. Can be called from any thread.
virtual void PostTask(std::function<void()>) = 0;
// Schedule a task for execution after |delay_ms|. Note that there is no
// strict ordering guarantee between immediate and delayed tasks. Can be
// called from any thread.
virtual void PostDelayedTask(std::function<void()>, uint32_t delay_ms) = 0;
// Schedule a task to run when the handle becomes readable. The same handle
// can only be monitored by one function. Note that this function only needs
// to be implemented on platforms where the built-in ipc framework is used.
// Can be called from any thread.
// TODO(skyostil): Refactor this out of the shared interface.
virtual void AddFileDescriptorWatch(PlatformHandle,
std::function<void()>) = 0;
// Remove a previously scheduled watch for the handle. If this is run on the
// target thread of this TaskRunner, guarantees that the task registered to
// this handle will not be executed after this function call.
// Can be called from any thread.
virtual void RemoveFileDescriptorWatch(PlatformHandle) = 0;
// Checks if the current thread is the same thread where the TaskRunner's task
// run. This allows single threaded task runners (like the ones used in
// perfetto) to inform the caller that anything posted will run on the same
// thread/sequence. This can allow some callers to skip PostTask and instead
// directly execute the code. Can be called from any thread.
virtual bool RunsTasksOnCurrentThread() const = 0;
};
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_BASE_TASK_RUNNER_H_

View file

@ -0,0 +1,75 @@
/*
* Copyright (C) 2021 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 INCLUDE_PERFETTO_BASE_TEMPLATE_UTIL_H_
#define INCLUDE_PERFETTO_BASE_TEMPLATE_UTIL_H_
#include <cstddef>
#include <type_traits>
namespace perfetto {
namespace base {
// Helper to express preferences in an overload set. If more than one overload
// is available for a given set of parameters the overload with the higher
// priority will be chosen.
template <size_t I>
struct priority_tag : priority_tag<I - 1> {};
template <>
struct priority_tag<0> {};
// enable_if_t is an implementation of std::enable_if_t from C++14.
//
// Specification:
// https://en.cppreference.com/w/cpp/types/enable_if
template <bool B, class T = void>
using enable_if_t = typename std::enable_if<B, T>::type;
// decay_t is an implementation of std::decay_t from C++14.
//
// Specification:
// https://en.cppreference.com/w/cpp/types/decay
template <class T>
using decay_t = typename std::decay<T>::type;
// remove_cvref is an implementation of std::remove_cvref from
// C++20.
//
// Specification:
// https://en.cppreference.com/w/cpp/types/remove_cvref
template <class T>
struct remove_cvref {
using type = typename std::remove_cv<typename std::remove_cv<
typename std::remove_reference<T>::type>::type>::type;
};
template <class T>
using remove_cvref_t = typename remove_cvref<T>::type;
// Check if a given type is a specialization of a given template:
// is_specialization<T, std::vector>::value.
template <typename Type, template <typename...> class Template>
struct is_specialization : std::false_type {};
template <template <typename...> class Ref, typename... Args>
struct is_specialization<Ref<Args...>, Ref> : std::true_type {};
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_BASE_TEMPLATE_UTIL_H_

View file

@ -0,0 +1,85 @@
/*
* Copyright (C) 2018 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 INCLUDE_PERFETTO_BASE_THREAD_UTILS_H_
#define INCLUDE_PERFETTO_BASE_THREAD_UTILS_H_
#include <stdint.h>
#include "perfetto/base/build_config.h"
#include "perfetto/base/export.h"
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
extern "C" {
// Prototype extracted from the Windows SDK to avoid including windows.h.
__declspec(dllimport) unsigned long __stdcall GetCurrentThreadId();
}
#elif PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
#include <zircon/types.h>
#elif PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#else
#include <pthread.h>
#endif
namespace perfetto {
namespace base {
#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
using PlatformThreadId = pid_t;
inline PlatformThreadId GetThreadId() {
return gettid();
}
#elif PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX)
using PlatformThreadId = pid_t;
inline PlatformThreadId GetThreadId() {
return static_cast<pid_t>(syscall(__NR_gettid));
}
#elif PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
using PlatformThreadId = zx_koid_t;
// Not inlined because the result is cached internally.
PERFETTO_EXPORT_COMPONENT PlatformThreadId GetThreadId();
#elif PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
using PlatformThreadId = uint64_t;
inline PlatformThreadId GetThreadId() {
uint64_t tid;
pthread_threadid_np(nullptr, &tid);
return tid;
}
#elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
using PlatformThreadId = uint64_t;
inline PlatformThreadId GetThreadId() {
return static_cast<uint64_t>(GetCurrentThreadId());
}
#elif PERFETTO_BUILDFLAG(PERFETTO_OS_NACL)
using PlatformThreadId = pid_t;
inline PlatformThreadId GetThreadId() {
return reinterpret_cast<int32_t>(pthread_self());
}
#else // Default to pthreads in case no OS is set.
using PlatformThreadId = pthread_t;
inline PlatformThreadId GetThreadId() {
return pthread_self();
}
#endif
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_BASE_THREAD_UTILS_H_

View file

@ -0,0 +1,294 @@
/*
* Copyright (C) 2018 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 INCLUDE_PERFETTO_BASE_TIME_H_
#define INCLUDE_PERFETTO_BASE_TIME_H_
#include <time.h>
#include <chrono>
#include <optional>
#include <string>
#include "perfetto/base/build_config.h"
#include "perfetto/base/logging.h"
#if PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
#include <mach/mach_init.h>
#include <mach/mach_port.h>
#include <mach/mach_time.h>
#include <mach/thread_act.h>
#endif
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WASM)
#include <emscripten/emscripten.h>
#endif
#if PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_X86_64)
#if PERFETTO_BUILDFLAG(PERFETTO_COMPILER_MSVC)
#include <intrin.h>
#else
#include <x86intrin.h>
#endif
#endif
namespace perfetto {
namespace base {
using TimeSeconds = std::chrono::seconds;
using TimeMillis = std::chrono::milliseconds;
using TimeNanos = std::chrono::nanoseconds;
inline TimeNanos FromPosixTimespec(const struct timespec& ts) {
return TimeNanos(ts.tv_sec * 1000000000LL + ts.tv_nsec);
}
void SleepMicroseconds(unsigned interval_us);
void InitializeTime();
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
TimeNanos GetWallTimeNs();
TimeNanos GetThreadCPUTimeNs();
inline TimeNanos GetWallTimeRawNs() {
return GetWallTimeNs();
}
// TODO: Clock that counts time during suspend is not implemented on Windows.
inline TimeNanos GetBootTimeNs() {
return GetWallTimeNs();
}
#elif PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
inline TimeNanos GetWallTimeNs() {
auto init_timebase_info = []() -> mach_timebase_info_data_t {
mach_timebase_info_data_t timebase_info;
mach_timebase_info(&timebase_info);
return timebase_info;
};
static mach_timebase_info_data_t timebase_info = init_timebase_info();
uint64_t mach_time = mach_absolute_time();
// Take the fast path when the conversion is 1:1. The result will for sure fit
// into an int_64 because we're going from nanoseconds to microseconds.
if (timebase_info.numer == timebase_info.denom) {
return TimeNanos(mach_time);
}
// Nanoseconds is mach_time * timebase.numer // timebase.denom. Divide first
// to reduce the chance of overflow. Also stash the remainder right now,
// a likely byproduct of the division.
uint64_t nanoseconds = mach_time / timebase_info.denom;
const uint64_t mach_time_remainder = mach_time % timebase_info.denom;
// Now multiply, keeping an eye out for overflow.
PERFETTO_CHECK(!__builtin_umulll_overflow(nanoseconds, timebase_info.numer,
&nanoseconds));
// By dividing first we lose precision. Regain it by adding back the
// nanoseconds from the remainder, with an eye out for overflow.
uint64_t least_significant_nanoseconds =
(mach_time_remainder * timebase_info.numer) / timebase_info.denom;
PERFETTO_CHECK(!__builtin_uaddll_overflow(
nanoseconds, least_significant_nanoseconds, &nanoseconds));
return TimeNanos(nanoseconds);
}
inline TimeNanos GetWallTimeRawNs() {
return GetWallTimeNs();
}
// TODO: Clock that counts time during suspend is not implemented on Mac.
inline TimeNanos GetBootTimeNs() {
return GetWallTimeNs();
}
// Before MacOS 10.12 clock_gettime() was not implemented.
#if __MAC_OS_X_VERSION_MIN_REQUIRED < 101200
inline TimeNanos GetThreadCPUTimeNs() {
mach_port_t this_thread = mach_thread_self();
mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
thread_basic_info_data_t info{};
kern_return_t kr =
thread_info(this_thread, THREAD_BASIC_INFO,
reinterpret_cast<thread_info_t>(&info), &count);
mach_port_deallocate(mach_task_self(), this_thread);
if (kr != KERN_SUCCESS) {
PERFETTO_DFATAL("Failed to get CPU time.");
return TimeNanos(0);
}
return TimeNanos(info.user_time.seconds * 1000000000LL +
info.user_time.microseconds * 1000LL +
info.system_time.seconds * 1000000000LL +
info.system_time.microseconds * 1000LL);
}
#else
inline TimeNanos GetThreadCPUTimeNs() {
struct timespec ts = {};
PERFETTO_CHECK(clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) == 0);
return FromPosixTimespec(ts);
}
#endif
#elif PERFETTO_BUILDFLAG(PERFETTO_OS_WASM)
inline TimeNanos GetWallTimeNs() {
return TimeNanos(static_cast<uint64_t>(emscripten_get_now()) * 1000000);
}
inline TimeNanos GetWallTimeRawNs() {
return GetWallTimeNs();
}
inline TimeNanos GetThreadCPUTimeNs() {
return TimeNanos(0);
}
// TODO: Clock that counts time during suspend is not implemented on WASM.
inline TimeNanos GetBootTimeNs() {
return GetWallTimeNs();
}
#elif PERFETTO_BUILDFLAG(PERFETTO_OS_NACL)
// Tracing time doesn't need to work on NaCl since its going away shortly. We
// just need to compile on it. The only function NaCl could support is
// GetWallTimeNs(), but to prevent false hope we leave it unimplemented.
inline TimeNanos GetWallTimeNs() {
return TimeNanos(0);
}
inline TimeNanos GetWallTimeRawNs() {
return TimeNanos(0);
}
inline TimeNanos GetThreadCPUTimeNs() {
return TimeNanos(0);
}
inline TimeNanos GetBootTimeNs() {
return TimeNanos(0);
}
#else // posix
constexpr clockid_t kWallTimeClockSource = CLOCK_MONOTONIC;
inline TimeNanos GetTimeInternalNs(clockid_t clk_id) {
struct timespec ts = {};
PERFETTO_CHECK(clock_gettime(clk_id, &ts) == 0);
return FromPosixTimespec(ts);
}
// Return ns from boot. Conversely to GetWallTimeNs, this clock counts also time
// during suspend (when supported).
inline TimeNanos GetBootTimeNs() {
// Determine if CLOCK_BOOTTIME is available on the first call.
static const clockid_t kBootTimeClockSource = [] {
struct timespec ts = {};
int res = clock_gettime(CLOCK_BOOTTIME, &ts);
return res == 0 ? CLOCK_BOOTTIME : kWallTimeClockSource;
}();
return GetTimeInternalNs(kBootTimeClockSource);
}
inline TimeNanos GetWallTimeNs() {
return GetTimeInternalNs(kWallTimeClockSource);
}
inline TimeNanos GetWallTimeRawNs() {
return GetTimeInternalNs(CLOCK_MONOTONIC_RAW);
}
inline TimeNanos GetThreadCPUTimeNs() {
return GetTimeInternalNs(CLOCK_THREAD_CPUTIME_ID);
}
#endif
inline TimeSeconds GetBootTimeS() {
return std::chrono::duration_cast<TimeSeconds>(GetBootTimeNs());
}
inline TimeMillis GetBootTimeMs() {
return std::chrono::duration_cast<TimeMillis>(GetBootTimeNs());
}
inline TimeMillis GetWallTimeMs() {
return std::chrono::duration_cast<TimeMillis>(GetWallTimeNs());
}
inline TimeSeconds GetWallTimeS() {
return std::chrono::duration_cast<TimeSeconds>(GetWallTimeNs());
}
inline struct timespec ToPosixTimespec(TimeMillis time) {
struct timespec ts {};
const long time_s = static_cast<long>(time.count() / 1000);
ts.tv_sec = time_s;
ts.tv_nsec = (static_cast<long>(time.count()) - time_s * 1000L) * 1000000L;
return ts;
}
std::string GetTimeFmt(const std::string& fmt);
inline int64_t TimeGm(struct tm* tms) {
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
return static_cast<int64_t>(_mkgmtime(tms));
#elif PERFETTO_BUILDFLAG(PERFETTO_OS_NACL)
// NaCL has no timegm.
if (tms) // Kinda if (true), but avoids "mark as noreturn" errors.
PERFETTO_FATAL("timegm not supported");
return -1;
#else
return static_cast<int64_t>(timegm(tms));
#endif
}
// Creates a time_t-compatible timestamp (seconds since epoch) from a tuple of
// y-m-d-h-m-s. It's a saner version of timegm(). Some remarks:
// The year is just the actual year (it's Y-1900 in timegm()).
// The month ranges 1-12 (it's 0-11 in timegm()).
inline int64_t MkTime(int year, int month, int day, int h, int m, int s) {
PERFETTO_DCHECK(year >= 1900);
PERFETTO_DCHECK(month > 0 && month <= 12);
PERFETTO_DCHECK(day > 0 && day <= 31);
struct tm tms {};
tms.tm_year = year - 1900;
tms.tm_mon = month - 1;
tms.tm_mday = day;
tms.tm_hour = h;
tms.tm_min = m;
tms.tm_sec = s;
return TimeGm(&tms);
}
#if PERFETTO_BUILDFLAG(PERFETTO_ARCH_CPU_X86_64)
inline uint64_t Rdtsc() {
return static_cast<uint64_t>(__rdtsc());
}
#endif
std::optional<int32_t> GetTimezoneOffsetMins();
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_BASE_TIME_H_

View file

@ -0,0 +1,73 @@
# Copyright (C) 2019 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.
import("../../../../gn/perfetto.gni")
source_set("base") {
sources = [
"android_utils.h",
"base64.h",
"circular_queue.h",
"container_annotations.h",
"crash_keys.h",
"ctrl_c_handler.h",
"endian.h",
"event_fd.h",
"file_utils.h",
"flat_hash_map.h",
"getopt.h",
"getopt_compat.h",
"hash.h",
"metatrace.h",
"metatrace_events.h",
"no_destructor.h",
"paged_memory.h",
"periodic_task.h",
"pipe.h",
"platform.h",
"scoped_file.h",
"scoped_mmap.h",
"small_set.h",
"small_vector.h",
"status_or.h",
"string_splitter.h",
"string_utils.h",
"string_view.h",
"string_writer.h",
"subprocess.h",
"sys_types.h",
"temp_file.h",
"thread_annotations.h",
"thread_checker.h",
"thread_task_runner.h",
"thread_utils.h",
"unix_task_runner.h",
"utils.h",
"uuid.h",
"waitable_event.h",
"watchdog.h",
"watchdog_noop.h",
"watchdog_posix.h",
"weak_ptr.h",
]
if (enable_perfetto_ipc) {
sources += [ "unix_socket.h" ]
}
public_configs = [ "../../../../gn:asan_instrumentation" ]
public_deps = [ "../../base" ]
}
source_set("version") {
sources = [ "version.h" ]
}

View file

@ -0,0 +1,39 @@
/*
* Copyright (C) 2021 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 INCLUDE_PERFETTO_EXT_BASE_ANDROID_UTILS_H_
#define INCLUDE_PERFETTO_EXT_BASE_ANDROID_UTILS_H_
#include <string>
#include "perfetto/base/build_config.h"
namespace perfetto {
namespace base {
#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
// Returns the value of the Android system property named `name`. If the
// property does not exist, returns an empty string (a non-existing property is
// the same as a property with an empty value for this API).
std::string GetAndroidProp(const char* name);
#endif // PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_ANDROID_UTILS_H_

View file

@ -0,0 +1,68 @@
/*
* Copyright (C) 2021 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 INCLUDE_PERFETTO_EXT_BASE_BASE64_H_
#define INCLUDE_PERFETTO_EXT_BASE_BASE64_H_
#include <optional>
#include <string>
#include "perfetto/ext/base/string_view.h"
#include "perfetto/ext/base/utils.h" // For ssize_t.
namespace perfetto {
namespace base {
// Returns the length of the destination string (included '=' padding).
// Does NOT include the size of the string null terminator.
inline size_t Base64EncSize(size_t src_size) {
return (src_size + 2) / 3 * 4;
}
// Returns the upper bound on the length of the destination buffer.
// The actual decoded length might be <= the number returned here.
inline size_t Base64DecSize(size_t src_size) {
return (src_size + 3) / 4 * 3;
}
// Does NOT null-terminate |dst|.
ssize_t Base64Encode(const void* src,
size_t src_size,
char* dst,
size_t dst_size);
std::string Base64Encode(const void* src, size_t src_size);
inline std::string Base64Encode(StringView sv) {
return Base64Encode(sv.data(), sv.size());
}
// Returns -1 in case of failure.
ssize_t Base64Decode(const char* src,
size_t src_size,
uint8_t* dst,
size_t dst_size);
std::optional<std::string> Base64Decode(const char* src, size_t src_size);
inline std::optional<std::string> Base64Decode(StringView sv) {
return Base64Decode(sv.data(), sv.size());
}
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_BASE64_H_

View file

@ -0,0 +1,337 @@
/*
* Copyright (C) 2019 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 INCLUDE_PERFETTO_EXT_BASE_CIRCULAR_QUEUE_H_
#define INCLUDE_PERFETTO_EXT_BASE_CIRCULAR_QUEUE_H_
#include <stdint.h>
#include <stdlib.h>
#include <cstddef>
#include <iterator>
#include "perfetto/base/logging.h"
#include "perfetto/ext/base/utils.h"
namespace perfetto {
namespace base {
// CircularQueue is a push-back-only / pop-front-only queue with the following
// characteristics:
// - The storage is based on a flat circular buffer. Beginning and end wrap
// as necessary, to keep pushes and pops O(1) as long as capacity expansion is
// not required.
// - Capacity is automatically expanded like in a std::vector. Expansion has a
// O(N) cost.
// - It allows random access, allowing in-place std::sort.
// - Iterators are not stable. Mutating the container invalidates all iterators.
// - It doesn't bother with const-correctness.
//
// Implementation details:
// Internally, |begin|, |end| and iterators use 64-bit monotonic indexes, which
// are incremented as if the queue was backed by unlimited storage.
// Even assuming that elements are inserted and removed every nanosecond, 64 bit
// is enough for 584 years.
// Wrapping happens only when addressing elements in the underlying circular
// storage. This limits the complexity and avoiding dealing with modular
// arithmetic all over the places.
template <class T>
class CircularQueue {
public:
class Iterator {
public:
using difference_type = ptrdiff_t;
using value_type = T;
using pointer = T*;
using reference = T&;
using iterator_category = std::random_access_iterator_tag;
Iterator(CircularQueue* queue, uint64_t pos, uint32_t generation)
: queue_(queue),
pos_(pos)
#if PERFETTO_DCHECK_IS_ON()
,
generation_(generation)
#endif
{
ignore_result(generation);
}
Iterator(const Iterator&) noexcept = default;
Iterator& operator=(const Iterator&) noexcept = default;
Iterator(Iterator&&) noexcept = default;
Iterator& operator=(Iterator&&) noexcept = default;
T* operator->() const {
#if PERFETTO_DCHECK_IS_ON()
PERFETTO_DCHECK(generation_ == queue_->generation());
#endif
return queue_->Get(pos_);
}
T& operator*() const { return *(operator->()); }
value_type& operator[](difference_type i) { return *(*this + i); }
Iterator& operator++() {
Add(1);
return *this;
}
Iterator operator++(int) {
Iterator ret = *this;
Add(1);
return ret;
}
Iterator& operator--() {
Add(-1);
return *this;
}
Iterator operator--(int) {
Iterator ret = *this;
Add(-1);
return ret;
}
friend Iterator operator+(const Iterator& iter, difference_type offset) {
Iterator ret = iter;
ret.Add(offset);
return ret;
}
Iterator& operator+=(difference_type offset) {
Add(offset);
return *this;
}
friend Iterator operator-(const Iterator& iter, difference_type offset) {
Iterator ret = iter;
ret.Add(-offset);
return ret;
}
Iterator& operator-=(difference_type offset) {
Add(-offset);
return *this;
}
friend ptrdiff_t operator-(const Iterator& lhs, const Iterator& rhs) {
return static_cast<ptrdiff_t>(lhs.pos_) -
static_cast<ptrdiff_t>(rhs.pos_);
}
friend bool operator==(const Iterator& lhs, const Iterator& rhs) {
return lhs.pos_ == rhs.pos_;
}
friend bool operator!=(const Iterator& lhs, const Iterator& rhs) {
return lhs.pos_ != rhs.pos_;
}
friend bool operator<(const Iterator& lhs, const Iterator& rhs) {
return lhs.pos_ < rhs.pos_;
}
friend bool operator<=(const Iterator& lhs, const Iterator& rhs) {
return lhs.pos_ <= rhs.pos_;
}
friend bool operator>(const Iterator& lhs, const Iterator& rhs) {
return lhs.pos_ > rhs.pos_;
}
friend bool operator>=(const Iterator& lhs, const Iterator& rhs) {
return lhs.pos_ >= rhs.pos_;
}
private:
inline void Add(difference_type offset) {
pos_ = static_cast<uint64_t>(static_cast<difference_type>(pos_) + offset);
PERFETTO_DCHECK(pos_ <= queue_->end_);
}
CircularQueue* queue_;
uint64_t pos_;
#if PERFETTO_DCHECK_IS_ON()
uint32_t generation_;
#endif
};
explicit CircularQueue(size_t initial_capacity = 1024) {
Grow(initial_capacity);
}
CircularQueue(CircularQueue&& other) noexcept
: entries_(std::move(other.entries_)),
capacity_(other.capacity_),
begin_(other.begin_),
end_(other.end_) {
increment_generation();
new (&other) CircularQueue(); // Reset the old queue so it's still usable.
}
CircularQueue& operator=(CircularQueue&& other) noexcept {
this->~CircularQueue(); // Destroy the current state.
new (this) CircularQueue(std::move(other)); // Use the move ctor above.
return *this;
}
explicit CircularQueue(const CircularQueue& other) noexcept {
Grow(other.capacity());
for (const auto& e : const_cast<CircularQueue&>(other))
emplace_back(e);
PERFETTO_DCHECK(size() == other.size());
}
CircularQueue& operator=(const CircularQueue& other) noexcept {
this->~CircularQueue(); // Destroy the current state.
new (this) CircularQueue(other); // Use the copy ctor above.
return *this;
}
~CircularQueue() {
if (!entries_) {
PERFETTO_DCHECK(empty());
return;
}
clear(); // Invoke destructors on all alive entries.
PERFETTO_DCHECK(empty());
}
template <typename... Args>
void emplace_back(Args&&... args) {
increment_generation();
if (PERFETTO_UNLIKELY(size() >= capacity_))
Grow();
T* slot = Get(end_++);
new (slot) T(std::forward<Args>(args)...);
}
void erase_front(size_t n) {
increment_generation();
for (; n && (begin_ < end_); --n) {
Get(begin_)->~T();
begin_++; // This needs to be its own statement, Get() checks begin_.
}
}
void pop_front() { erase_front(1); }
void clear() { erase_front(size()); }
void shrink_to_fit() {
// We only bother shrinking if we can fit in quarter of the capacity we are
// currently using. Moreover, don't bother shrinking below 4096 elements as
// that will cause a lot of reallocations for little benefit.
if (size() > capacity() / 2 || capacity() <= 4096) {
return;
}
ChangeCapacity(capacity() / 2);
}
T& at(size_t idx) {
PERFETTO_DCHECK(idx < size());
return *Get(begin_ + idx);
}
Iterator begin() { return Iterator(this, begin_, generation()); }
Iterator end() { return Iterator(this, end_, generation()); }
T& front() { return *begin(); }
T& back() { return *(end() - 1); }
bool empty() const { return size() == 0; }
size_t size() const {
PERFETTO_DCHECK(end_ - begin_ <= capacity_);
return static_cast<size_t>(end_ - begin_);
}
size_t capacity() const { return capacity_; }
#if PERFETTO_DCHECK_IS_ON()
uint32_t generation() const { return generation_; }
void increment_generation() { ++generation_; }
#else
uint32_t generation() const { return 0; }
void increment_generation() {}
#endif
private:
void Grow(size_t new_capacity = 0) {
// Capacity must be always a power of two. This allows Get() to use a simple
// bitwise-AND for handling the wrapping instead of a full division.
new_capacity = new_capacity ? new_capacity : capacity_ * 2;
PERFETTO_CHECK((new_capacity & (new_capacity - 1)) == 0); // Must be pow2.
// On 32-bit systems this might hit the 4GB wall and overflow. We can't do
// anything other than crash in this case.
PERFETTO_CHECK(new_capacity > capacity_);
ChangeCapacity(new_capacity);
}
void ChangeCapacity(size_t new_capacity) {
// We should still have enough space to fit all the elements in the queue.
PERFETTO_CHECK(new_capacity >= size());
AlignedUniquePtr<T[]> new_vec = AlignedAllocTyped<T[]>(new_capacity);
// Move all elements in the expanded array.
size_t new_size = 0;
for (uint64_t i = begin_; i < end_; i++)
new (&new_vec[new_size++]) T(std::move(*Get(i))); // Placement move ctor.
// Even if all the elements are std::move()-d and likely empty, we are still
// required to call the dtor for them.
for (uint64_t i = begin_; i < end_; i++)
Get(i)->~T();
begin_ = 0;
end_ = new_size;
capacity_ = new_capacity;
entries_ = std::move(new_vec);
}
inline T* Get(uint64_t pos) {
PERFETTO_DCHECK(pos >= begin_ && pos < end_);
PERFETTO_DCHECK((capacity_ & (capacity_ - 1)) == 0); // Must be a pow2.
auto index = static_cast<size_t>(pos & (capacity_ - 1));
return &entries_[index];
}
// Underlying storage. It's raw malloc-ed rather than being a unique_ptr<T[]>
// to allow having uninitialized entries inside it.
AlignedUniquePtr<T[]> entries_;
size_t capacity_ = 0; // Number of allocated slots (NOT bytes) in |entries_|.
// The |begin_| and |end_| indexes are monotonic and never wrap. Modular arith
// is used only when dereferencing entries in the vector.
uint64_t begin_ = 0;
uint64_t end_ = 0;
// Generation is used in debug builds only for checking iterator validity.
#if PERFETTO_DCHECK_IS_ON()
uint32_t generation_ = 0;
#endif
};
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_CIRCULAR_QUEUE_H_

View file

@ -0,0 +1,59 @@
/*
* Copyright (C) 2018 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 INCLUDE_PERFETTO_EXT_BASE_CONTAINER_ANNOTATIONS_H_
#define INCLUDE_PERFETTO_EXT_BASE_CONTAINER_ANNOTATIONS_H_
#include "perfetto/base/build_config.h"
// Windows ASAN doesn't currently support these annotations.
#if defined(ADDRESS_SANITIZER) && !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) && \
!defined(ADDRESS_SANITIZER_WITHOUT_INSTRUMENTATION)
#include <sanitizer/common_interface_defs.h>
#define ANNOTATE_NEW_BUFFER(buffer, capacity, new_size) \
if (buffer) { \
__sanitizer_annotate_contiguous_container(buffer, (buffer) + (capacity), \
(buffer) + (capacity), \
(buffer) + (new_size)); \
}
#define ANNOTATE_DELETE_BUFFER(buffer, capacity, old_size) \
if (buffer) { \
__sanitizer_annotate_contiguous_container(buffer, (buffer) + (capacity), \
(buffer) + (old_size), \
(buffer) + (capacity)); \
}
#define ANNOTATE_CHANGE_SIZE(buffer, capacity, old_size, new_size) \
if (buffer) { \
__sanitizer_annotate_contiguous_container(buffer, (buffer) + (capacity), \
(buffer) + (old_size), \
(buffer) + (new_size)); \
}
#define ANNOTATE_CHANGE_CAPACITY(buffer, old_capacity, buffer_size, \
new_capacity) \
ANNOTATE_DELETE_BUFFER(buffer, old_capacity, buffer_size); \
ANNOTATE_NEW_BUFFER(buffer, new_capacity, buffer_size);
// Annotations require buffers to begin on an 8-byte boundary.
#else // defined(ADDRESS_SANITIZER)
#define ANNOTATE_NEW_BUFFER(buffer, capacity, new_size)
#define ANNOTATE_DELETE_BUFFER(buffer, capacity, old_size)
#define ANNOTATE_CHANGE_SIZE(buffer, capacity, old_size, new_size)
#define ANNOTATE_CHANGE_CAPACITY(buffer, old_capacity, buffer_size, \
new_capacity)
#endif // defined(ADDRESS_SANITIZER)
#endif // INCLUDE_PERFETTO_EXT_BASE_CONTAINER_ANNOTATIONS_H_

View file

@ -0,0 +1,162 @@
/*
* Copyright (C) 2021 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 INCLUDE_PERFETTO_EXT_BASE_CRASH_KEYS_H_
#define INCLUDE_PERFETTO_EXT_BASE_CRASH_KEYS_H_
#include <algorithm>
#include <atomic>
#include <stdint.h>
#include <string.h>
#include "perfetto/base/compiler.h"
#include "perfetto/ext/base/string_view.h"
// Crash keys are very simple global variables with static-storage that
// are reported on crash time for managed crashes (CHECK/FATAL/Watchdog).
// - Translation units can define a CrashKey and register it at some point
// during initialization.
// - CrashKey instances must be long-lived. They should really be just global
// static variable in the anonymous namespace.
// Example:
// subsystem_1.cc
// CrashKey g_client_id("ipc_client_id");
// ...
// OnIpcReceived(client_id) {
// g_client_id.Set(client_id);
// ... // Process the IPC
// g_client_id.Clear();
// }
// Or equivalently:
// OnIpcReceived(client_id) {
// auto scoped_key = g_client_id.SetScoped(client_id);
// ... // Process the IPC
// }
//
// If a crash happens while processing the IPC, the crash report will
// have a line "ipc_client_id: 42".
//
// Thread safety considerations:
// CrashKeys can be registered and set/cleared from any thread.
// There is no compelling use-case to have full acquire/release consistency when
// setting a key. This means that if a thread crashes immediately after a
// crash key has been set on another thread, the value printed on the crash
// report could be incomplete. The code guarantees defined behavior and does
// not rely on null-terminated string (in the worst case 32 bytes of random
// garbage will be printed out).
// The tests live in logging_unittest.cc.
namespace perfetto {
namespace base {
constexpr size_t kCrashKeyMaxStrSize = 32;
// CrashKey instances must be long lived
class CrashKey {
public:
class ScopedClear {
public:
explicit ScopedClear(CrashKey* k) : key_(k) {}
~ScopedClear() {
if (key_)
key_->Clear();
}
ScopedClear(const ScopedClear&) = delete;
ScopedClear& operator=(const ScopedClear&) = delete;
ScopedClear& operator=(ScopedClear&&) = delete;
ScopedClear(ScopedClear&& other) noexcept : key_(other.key_) {
other.key_ = nullptr;
}
private:
CrashKey* key_;
};
// constexpr so it can be used in the anon namespace without requiring a
// global constructor.
// |name| must be a long-lived string.
constexpr explicit CrashKey(const char* name)
: registered_{}, type_(Type::kUnset), name_(name), str_value_{} {}
CrashKey(const CrashKey&) = delete;
CrashKey& operator=(const CrashKey&) = delete;
CrashKey(CrashKey&&) = delete;
CrashKey& operator=(CrashKey&&) = delete;
enum class Type : uint8_t { kUnset = 0, kInt, kStr };
void Clear() {
int_value_.store(0, std::memory_order_relaxed);
type_.store(Type::kUnset, std::memory_order_relaxed);
}
void Set(int64_t value) {
int_value_.store(value, std::memory_order_relaxed);
type_.store(Type::kInt, std::memory_order_relaxed);
if (PERFETTO_UNLIKELY(!registered_.load(std::memory_order_relaxed)))
Register();
}
void Set(StringView sv) {
size_t len = std::min(sv.size(), sizeof(str_value_) - 1);
for (size_t i = 0; i < len; ++i)
str_value_[i].store(sv.data()[i], std::memory_order_relaxed);
str_value_[len].store('\0', std::memory_order_relaxed);
type_.store(Type::kStr, std::memory_order_relaxed);
if (PERFETTO_UNLIKELY(!registered_.load(std::memory_order_relaxed)))
Register();
}
ScopedClear SetScoped(int64_t value) PERFETTO_WARN_UNUSED_RESULT {
Set(value);
return ScopedClear(this);
}
ScopedClear SetScoped(StringView sv) PERFETTO_WARN_UNUSED_RESULT {
Set(sv);
return ScopedClear(this);
}
void Register();
int64_t int_value() const {
return int_value_.load(std::memory_order_relaxed);
}
size_t ToString(char* dst, size_t len);
private:
std::atomic<bool> registered_;
std::atomic<Type> type_;
const char* const name_;
union {
std::atomic<char> str_value_[kCrashKeyMaxStrSize];
std::atomic<int64_t> int_value_;
};
};
// Fills |dst| with a string containing one line for each crash key
// (excluding the unset ones).
// Returns number of chars written, without counting the NUL terminator.
// This is used in logging.cc when emitting the crash report abort message.
size_t SerializeCrashKeys(char* dst, size_t len);
void UnregisterAllCrashKeysForTesting();
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_CRASH_KEYS_H_

View file

@ -0,0 +1,32 @@
/*
* Copyright (C) 2021 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 INCLUDE_PERFETTO_EXT_BASE_CTRL_C_HANDLER_H_
#define INCLUDE_PERFETTO_EXT_BASE_CTRL_C_HANDLER_H_
namespace perfetto {
namespace base {
// On Linux/Android/Mac: installs SIGINT + SIGTERM signal handlers.
// On Windows: installs a SetConsoleCtrlHandler() handler.
// The passed handler must be async safe.
using CtrlCHandlerFunction = void (*)();
void InstallCtrlCHandler(CtrlCHandlerFunction);
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_CTRL_C_HANDLER_H_

View file

@ -0,0 +1,58 @@
/*
* 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 INCLUDE_PERFETTO_EXT_BASE_ENDIAN_H_
#define INCLUDE_PERFETTO_EXT_BASE_ENDIAN_H_
#include <stdint.h>
#include <stdlib.h> // For MSVC
#include "perfetto/base/build_config.h"
#include "perfetto/base/compiler.h"
#if !PERFETTO_IS_LITTLE_ENDIAN()
#error "endian.h supports only little-endian archs"
#endif
namespace perfetto {
namespace base {
#if PERFETTO_BUILDFLAG(PERFETTO_COMPILER_MSVC)
inline uint16_t HostToBE16(uint16_t x) {
return _byteswap_ushort(x);
}
inline uint32_t HostToBE32(uint32_t x) {
return _byteswap_ulong(x);
}
inline uint64_t HostToBE64(uint64_t x) {
return _byteswap_uint64(x);
}
#else
inline uint16_t HostToBE16(uint16_t x) {
return __builtin_bswap16(x);
}
inline uint32_t HostToBE32(uint32_t x) {
return __builtin_bswap32(x);
}
inline uint64_t HostToBE64(uint64_t x) {
return __builtin_bswap64(x);
}
#endif
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_ENDIAN_H_

View file

@ -0,0 +1,64 @@
/*
* Copyright (C) 2018 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 INCLUDE_PERFETTO_EXT_BASE_EVENT_FD_H_
#define INCLUDE_PERFETTO_EXT_BASE_EVENT_FD_H_
#include "perfetto/base/build_config.h"
#include "perfetto/base/platform_handle.h"
#include "perfetto/ext/base/scoped_file.h"
namespace perfetto {
namespace base {
// A waitable event that can be used with poll/select.
// This is really a wrapper around eventfd_create with a pipe-based fallback
// for other platforms where eventfd is not supported.
class EventFd {
public:
EventFd();
~EventFd();
EventFd(EventFd&&) noexcept = default;
EventFd& operator=(EventFd&&) = default;
// The non-blocking file descriptor that can be polled to wait for the event.
PlatformHandle fd() const { return event_handle_.get(); }
// Can be called from any thread.
void Notify();
// Can be called from any thread. If more Notify() are queued a Clear() call
// can clear all of them (up to 16 per call).
void Clear();
private:
// The eventfd, when eventfd is supported, otherwise this is the read end of
// the pipe for fallback mode.
ScopedPlatformHandle event_handle_;
#if !PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) && \
!PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && \
!PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
// On Mac and other non-Linux UNIX platforms a pipe-based fallback is used.
// The write end of the wakeup pipe.
ScopedFile write_fd_;
#endif
};
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_EVENT_FD_H_

View file

@ -0,0 +1,114 @@
/*
* Copyright (C) 2018 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 INCLUDE_PERFETTO_EXT_BASE_FILE_UTILS_H_
#define INCLUDE_PERFETTO_EXT_BASE_FILE_UTILS_H_
#include <fcntl.h> // For mode_t & O_RDONLY/RDWR. Exists also on Windows.
#include <stddef.h>
#include <optional>
#include <string>
#include <vector>
#include "perfetto/base/build_config.h"
#include "perfetto/base/export.h"
#include "perfetto/base/status.h"
#include "perfetto/ext/base/scoped_file.h"
#include "perfetto/ext/base/utils.h"
namespace perfetto {
namespace base {
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
using FileOpenMode = int;
inline constexpr char kDevNull[] = "NUL";
#else
using FileOpenMode = mode_t;
inline constexpr char kDevNull[] = "/dev/null";
#endif
constexpr FileOpenMode kFileModeInvalid = static_cast<FileOpenMode>(-1);
bool ReadPlatformHandle(PlatformHandle, std::string* out);
bool ReadFileDescriptor(int fd, std::string* out);
bool ReadFileStream(FILE* f, std::string* out);
bool ReadFile(const std::string& path, std::string* out);
// A wrapper around read(2). It deals with Linux vs Windows includes. It also
// deals with handling EINTR. Has the same semantics of UNIX's read(2).
ssize_t Read(int fd, void* dst, size_t dst_size);
// Call write until all data is written or an error is detected.
//
// man 2 write:
// If a write() is interrupted by a signal handler before any bytes are
// written, then the call fails with the error EINTR; if it is
// interrupted after at least one byte has been written, the call
// succeeds, and returns the number of bytes written.
ssize_t WriteAll(int fd, const void* buf, size_t count);
ssize_t WriteAllHandle(PlatformHandle, const void* buf, size_t count);
ScopedFile OpenFile(const std::string& path,
int flags,
FileOpenMode = kFileModeInvalid);
ScopedFstream OpenFstream(const char* path, const char* mode);
// This is an alias for close(). It's to avoid leaking Windows.h in headers.
// Exported because ScopedFile is used in the /include/ext API by Chromium
// component builds.
int PERFETTO_EXPORT_COMPONENT CloseFile(int fd);
bool FlushFile(int fd);
// Returns true if mkdir succeeds, false if it fails (see errno in that case).
bool Mkdir(const std::string& path);
// Calls rmdir() on UNIX, _rmdir() on Windows.
bool Rmdir(const std::string& path);
// Wrapper around access(path, F_OK).
bool FileExists(const std::string& path);
// Gets the extension for a filename. If the file has two extensions, returns
// only the last one (foo.pb.gz => .gz). Returns empty string if there is no
// extension.
std::string GetFileExtension(const std::string& filename);
// Puts the path to all files under |dir_path| in |output|, recursively walking
// subdirectories. File paths are relative to |dir_path|. Only files are
// included, not directories. Path separator is always '/', even on windows (not
// '\').
base::Status ListFilesRecursive(const std::string& dir_path,
std::vector<std::string>& output);
// Sets |path|'s owner group to |group_name| and permission mode bits to
// |mode_bits|.
base::Status SetFilePermissions(const std::string& path,
const std::string& group_name,
const std::string& mode_bits);
// Returns the size of the file located at |path|, or nullopt in case of error.
std::optional<uint64_t> GetFileSize(const std::string& path);
// Returns the size of the open file |fd|, or nullopt in case of error.
std::optional<uint64_t> GetFileSize(PlatformHandle fd);
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_FILE_UTILS_H_

View file

@ -0,0 +1,395 @@
/*
* Copyright (C) 2021 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 INCLUDE_PERFETTO_EXT_BASE_FLAT_HASH_MAP_H_
#define INCLUDE_PERFETTO_EXT_BASE_FLAT_HASH_MAP_H_
#include "perfetto/base/compiler.h"
#include "perfetto/base/logging.h"
#include "perfetto/ext/base/hash.h"
#include "perfetto/ext/base/utils.h"
#include <algorithm>
#include <functional>
#include <limits>
namespace perfetto {
namespace base {
// An open-addressing hashmap implementation.
// Pointers are not stable, neither for keys nor values.
// Has similar performances of a RobinHood hash (without the complications)
// and 2x an unordered map.
// Doc: http://go/perfetto-hashtables .
//
// When used to implement a string pool in TraceProcessor, the performance
// characteristics obtained by replaying the set of strings seeen in a 4GB trace
// (226M strings, 1M unique) are the following (see flat_hash_map_benchmark.cc):
// This(Linear+AppendOnly) 879,383,676 ns 258.013M insertions/s
// This(LinearProbe): 909,206,047 ns 249.546M insertions/s
// This(QuadraticProbe): 1,083,844,388 ns 209.363M insertions/s
// std::unordered_map: 6,203,351,870 ns 36.5811M insertions/s
// tsl::robin_map: 931,403,397 ns 243.622M insertions/s
// absl::flat_hash_map: 998,013,459 ns 227.379M insertions/s
// FollyF14FastMap: 1,181,480,602 ns 192.074M insertions/s
// The structs below define the probing algorithm used to probe slots upon a
// collision. They are guaranteed to visit all slots as our table size is always
// a power of two (see https://en.wikipedia.org/wiki/Quadratic_probing).
// Linear probing can be faster if the hashing is well distributed and the load
// is not high. For TraceProcessor's StringPool this is the fastest. It can
// degenerate badly if the hashing doesn't spread (e.g., if using directly pids
// as keys, with a no-op hashing function).
struct LinearProbe {
static inline size_t Calc(size_t key_hash, size_t step, size_t capacity) {
return (key_hash + step) & (capacity - 1); // Linear probe
}
};
// Generates the sequence: 0, 3, 10, 21, 36, 55, ...
// Can be a bit (~5%) slower than LinearProbe because it's less cache hot, but
// avoids degenerating badly if the hash function is bad and causes clusters.
// A good default choice unless benchmarks prove otherwise.
struct QuadraticProbe {
static inline size_t Calc(size_t key_hash, size_t step, size_t capacity) {
return (key_hash + 2 * step * step + step) & (capacity - 1);
}
};
// Tends to perform in the middle between linear and quadratic.
// It's a bit more cache-effective than the QuadraticProbe but can create more
// clustering if the hash function doesn't spread well.
// Generates the sequence: 0, 1, 3, 6, 10, 15, 21, ...
struct QuadraticHalfProbe {
static inline size_t Calc(size_t key_hash, size_t step, size_t capacity) {
return (key_hash + (step * step + step) / 2) & (capacity - 1);
}
};
template <typename Key,
typename Value,
typename Hasher = base::Hash<Key>,
typename Probe = QuadraticProbe,
bool AppendOnly = false>
class FlatHashMap {
public:
class Iterator {
public:
explicit Iterator(const FlatHashMap* map) : map_(map) { FindNextNonFree(); }
~Iterator() = default;
Iterator(const Iterator&) = default;
Iterator& operator=(const Iterator&) = default;
Iterator(Iterator&&) noexcept = default;
Iterator& operator=(Iterator&&) noexcept = default;
Key& key() { return map_->keys_[idx_]; }
Value& value() { return map_->values_[idx_]; }
const Key& key() const { return map_->keys_[idx_]; }
const Value& value() const { return map_->values_[idx_]; }
explicit operator bool() const { return idx_ != kEnd; }
Iterator& operator++() {
PERFETTO_DCHECK(idx_ < map_->capacity_);
++idx_;
FindNextNonFree();
return *this;
}
private:
static constexpr size_t kEnd = std::numeric_limits<size_t>::max();
void FindNextNonFree() {
const auto& tags = map_->tags_;
for (; idx_ < map_->capacity_; idx_++) {
if (tags[idx_] != kFreeSlot && (AppendOnly || tags[idx_] != kTombstone))
return;
}
idx_ = kEnd;
}
const FlatHashMap* map_ = nullptr;
size_t idx_ = 0;
}; // Iterator
static constexpr int kDefaultLoadLimitPct = 75;
explicit FlatHashMap(size_t initial_capacity = 0,
int load_limit_pct = kDefaultLoadLimitPct)
: load_limit_percent_(load_limit_pct) {
if (initial_capacity > 0)
Reset(initial_capacity);
}
// We are calling Clear() so that the destructors for the inserted entries are
// called (unless they are trivial, in which case it will be a no-op).
~FlatHashMap() { Clear(); }
FlatHashMap(FlatHashMap&& other) noexcept {
tags_ = std::move(other.tags_);
keys_ = std::move(other.keys_);
values_ = std::move(other.values_);
capacity_ = other.capacity_;
size_ = other.size_;
max_probe_length_ = other.max_probe_length_;
load_limit_ = other.load_limit_;
load_limit_percent_ = other.load_limit_percent_;
new (&other) FlatHashMap();
}
FlatHashMap& operator=(FlatHashMap&& other) noexcept {
this->~FlatHashMap();
new (this) FlatHashMap(std::move(other));
return *this;
}
FlatHashMap(const FlatHashMap&) = delete;
FlatHashMap& operator=(const FlatHashMap&) = delete;
std::pair<Value*, bool> Insert(Key key, Value value) {
const size_t key_hash = Hasher{}(key);
const uint8_t tag = HashToTag(key_hash);
static constexpr size_t kSlotNotFound = std::numeric_limits<size_t>::max();
// This for loop does in reality at most two attempts:
// The first iteration either:
// - Early-returns, because the key exists already,
// - Finds an insertion slot and proceeds because the load is < limit.
// The second iteration is only hit in the unlikely case of this insertion
// bringing the table beyond the target |load_limit_| (or the edge case
// of the HT being full, if |load_limit_pct_| = 100).
// We cannot simply pre-grow the table before insertion, because we must
// guarantee that calling Insert() with a key that already exists doesn't
// invalidate iterators.
size_t insertion_slot;
size_t probe_len;
for (;;) {
PERFETTO_DCHECK((capacity_ & (capacity_ - 1)) == 0); // Must be a pow2.
insertion_slot = kSlotNotFound;
// Start the iteration at the desired slot (key_hash % capacity_)
// searching either for a free slot or a tombstone. In the worst case we
// might end up scanning the whole array of slots. The Probe functions are
// guaranteed to visit all the slots within |capacity_| steps. If we find
// a free slot, we can stop the search immediately (a free slot acts as an
// "end of chain for entries having the same hash". If we find a
// tombstones (a deleted slot) we remember its position, but have to keep
// searching until a free slot to make sure we don't insert a duplicate
// key.
for (probe_len = 0; probe_len < capacity_;) {
const size_t idx = Probe::Calc(key_hash, probe_len, capacity_);
PERFETTO_DCHECK(idx < capacity_);
const uint8_t tag_idx = tags_[idx];
++probe_len;
if (tag_idx == kFreeSlot) {
// Rationale for "insertion_slot == kSlotNotFound": if we encountered
// a tombstone while iterating we should reuse that rather than
// taking another slot.
if (AppendOnly || insertion_slot == kSlotNotFound)
insertion_slot = idx;
break;
}
// We should never encounter tombstones in AppendOnly mode.
PERFETTO_DCHECK(!(tag_idx == kTombstone && AppendOnly));
if (!AppendOnly && tag_idx == kTombstone) {
insertion_slot = idx;
continue;
}
if (tag_idx == tag && keys_[idx] == key) {
// The key is already in the map.
return std::make_pair(&values_[idx], false);
}
} // for (idx)
// If we got to this point the key does not exist (otherwise we would have
// hit the return above) and we are going to insert a new entry.
// Before doing so, ensure we stay under the target load limit.
if (PERFETTO_UNLIKELY(size_ >= load_limit_)) {
MaybeGrowAndRehash(/*grow=*/true);
continue;
}
PERFETTO_DCHECK(insertion_slot != kSlotNotFound);
break;
} // for (attempt)
PERFETTO_CHECK(insertion_slot < capacity_);
// We found a free slot (or a tombstone). Proceed with the insertion.
Value* value_idx = &values_[insertion_slot];
new (&keys_[insertion_slot]) Key(std::move(key));
new (value_idx) Value(std::move(value));
tags_[insertion_slot] = tag;
PERFETTO_DCHECK(probe_len > 0 && probe_len <= capacity_);
max_probe_length_ = std::max(max_probe_length_, probe_len);
size_++;
return std::make_pair(value_idx, true);
}
Value* Find(const Key& key) const {
const size_t idx = FindInternal(key);
if (idx == kNotFound)
return nullptr;
return &values_[idx];
}
bool Erase(const Key& key) {
if (AppendOnly)
PERFETTO_FATAL("Erase() not supported because AppendOnly=true");
size_t idx = FindInternal(key);
if (idx == kNotFound)
return false;
EraseInternal(idx);
return true;
}
void Clear() {
// Avoid trivial heap operations on zero-capacity std::move()-d objects.
if (PERFETTO_UNLIKELY(capacity_ == 0))
return;
for (size_t i = 0; i < capacity_; ++i) {
const uint8_t tag = tags_[i];
if (tag != kFreeSlot && tag != kTombstone)
EraseInternal(i);
}
// Clear all tombstones. We really need to do this for AppendOnly.
MaybeGrowAndRehash(/*grow=*/false);
}
Value& operator[](Key key) {
auto it_and_inserted = Insert(std::move(key), Value{});
return *it_and_inserted.first;
}
Iterator GetIterator() { return Iterator(this); }
const Iterator GetIterator() const { return Iterator(this); }
size_t size() const { return size_; }
size_t capacity() const { return capacity_; }
// "protected" here is only for the flat_hash_map_benchmark.cc. Everything
// below is by all means private.
protected:
enum ReservedTags : uint8_t { kFreeSlot = 0, kTombstone = 1 };
static constexpr size_t kNotFound = std::numeric_limits<size_t>::max();
size_t FindInternal(const Key& key) const {
const size_t key_hash = Hasher{}(key);
const uint8_t tag = HashToTag(key_hash);
PERFETTO_DCHECK((capacity_ & (capacity_ - 1)) == 0); // Must be a pow2.
PERFETTO_DCHECK(max_probe_length_ <= capacity_);
for (size_t i = 0; i < max_probe_length_; ++i) {
const size_t idx = Probe::Calc(key_hash, i, capacity_);
const uint8_t tag_idx = tags_[idx];
if (tag_idx == kFreeSlot)
return kNotFound;
// HashToTag() never returns kTombstone, so the tag-check below cannot
// possibly match. Also we just want to skip tombstones.
if (tag_idx == tag && keys_[idx] == key) {
PERFETTO_DCHECK(tag_idx > kTombstone);
return idx;
}
} // for (idx)
return kNotFound;
}
void EraseInternal(size_t idx) {
PERFETTO_DCHECK(tags_[idx] > kTombstone);
PERFETTO_DCHECK(size_ > 0);
tags_[idx] = kTombstone;
keys_[idx].~Key();
values_[idx].~Value();
size_--;
}
PERFETTO_NO_INLINE void MaybeGrowAndRehash(bool grow) {
PERFETTO_DCHECK(size_ <= capacity_);
const size_t old_capacity = capacity_;
// Grow quickly up to 1MB, then chill.
const size_t old_size_bytes = old_capacity * (sizeof(Key) + sizeof(Value));
const size_t grow_factor = old_size_bytes < (1024u * 1024u) ? 8 : 2;
const size_t new_capacity =
grow ? std::max(old_capacity * grow_factor, size_t(1024))
: old_capacity;
auto old_tags(std::move(tags_));
auto old_keys(std::move(keys_));
auto old_values(std::move(values_));
size_t old_size = size_;
// This must be a CHECK (i.e. not just a DCHECK) to prevent UAF attacks on
// 32-bit archs that try to double the size of the table until wrapping.
PERFETTO_CHECK(new_capacity >= old_capacity);
Reset(new_capacity);
size_t new_size = 0; // Recompute the size.
for (size_t i = 0; i < old_capacity; ++i) {
const uint8_t old_tag = old_tags[i];
if (old_tag != kFreeSlot && old_tag != kTombstone) {
Insert(std::move(old_keys[i]), std::move(old_values[i]));
old_keys[i].~Key(); // Destroy the old objects.
old_values[i].~Value();
new_size++;
}
}
PERFETTO_DCHECK(new_size == old_size);
size_ = new_size;
}
// Doesn't call destructors. Use Clear() for that.
PERFETTO_NO_INLINE void Reset(size_t n) {
PERFETTO_DCHECK((n & (n - 1)) == 0); // Must be a pow2.
capacity_ = n;
max_probe_length_ = 0;
size_ = 0;
load_limit_ = n * static_cast<size_t>(load_limit_percent_) / 100;
load_limit_ = std::min(load_limit_, n);
tags_.reset(new uint8_t[n]);
memset(&tags_[0], 0, n); // Clear all tags.
keys_ = AlignedAllocTyped<Key[]>(n); // Deliberately not 0-initialized.
values_ = AlignedAllocTyped<Value[]>(n); // Deliberately not 0-initialized.
}
static inline uint8_t HashToTag(size_t full_hash) {
uint8_t tag = full_hash >> (sizeof(full_hash) * 8 - 8);
// Ensure the hash is always >= 2. We use 0, 1 for kFreeSlot and kTombstone.
tag += (tag <= kTombstone) << 1;
PERFETTO_DCHECK(tag > kTombstone);
return tag;
}
size_t capacity_ = 0;
size_t size_ = 0;
size_t max_probe_length_ = 0;
size_t load_limit_ = 0; // Updated every time |capacity_| changes.
int load_limit_percent_ =
kDefaultLoadLimitPct; // Load factor limit in % of |capacity_|.
// These arrays have always the |capacity_| elements.
// Note: AlignedUniquePtr just allocates memory, doesn't invoke any ctor/dtor.
std::unique_ptr<uint8_t[]> tags_;
AlignedUniquePtr<Key[]> keys_;
AlignedUniquePtr<Value[]> values_;
};
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_FLAT_HASH_MAP_H_

View file

@ -0,0 +1,51 @@
/*
* Copyright (C) 2021 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 INCLUDE_PERFETTO_EXT_BASE_GETOPT_H_
#define INCLUDE_PERFETTO_EXT_BASE_GETOPT_H_
// This is the header that should be included in all places that need getopt.h.
// This either routes on the sysroot getopt.h, for OSes that have one (all but
// Windows) or routes on the home-brewed getopt_compat.h.
#include "perfetto/base/build_config.h"
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
#include "perfetto/ext/base/getopt_compat.h"
// getopt_compat.h puts everything in a nested namespace, to allow
// getopt_compat_unittest.cc to use both <getopt.h> and "getopt_compat.h"
// without collisions.
// Here we expose them into the root namespace, because we want offer a drop-in
// replacement to the various main.cc, which can't know about the nested
// namespace.
using ::perfetto::base::getopt_compat::optarg;
using ::perfetto::base::getopt_compat::opterr;
using ::perfetto::base::getopt_compat::optind;
using ::perfetto::base::getopt_compat::option;
using ::perfetto::base::getopt_compat::optopt;
constexpr auto getopt = ::perfetto::base::getopt_compat::getopt;
constexpr auto getopt_long = ::perfetto::base::getopt_compat::getopt_long;
constexpr auto no_argument = ::perfetto::base::getopt_compat::no_argument;
constexpr auto required_argument =
::perfetto::base::getopt_compat::required_argument;
#else
#include <getopt.h>
#endif
#endif // INCLUDE_PERFETTO_EXT_BASE_GETOPT_H_

View file

@ -0,0 +1,71 @@
/*
* Copyright (C) 2021 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 INCLUDE_PERFETTO_EXT_BASE_GETOPT_COMPAT_H_
#define INCLUDE_PERFETTO_EXT_BASE_GETOPT_COMPAT_H_
#include <cstddef> // For std::nullptr_t
// No translation units other than base/getopt.h and getopt_compat_unittest.cc
// should directly include this file. Use base/getopt.h instead.
namespace perfetto {
namespace base {
namespace getopt_compat {
// A tiny getopt() replacement for Windows, which doesn't have <getopt.h>.
// This implementation is based on the subset of features that we use in the
// Perfetto codebase. It doesn't even try to deal with the full surface of GNU's
// getopt().
// Limitations:
// - getopt_long_only() is not supported.
// - optional_argument is not supported. That is extremely subtle and caused us
// problems in the past with GNU's getopt.
// - It does not reorder non-option arguments. It behaves like MacOS getopt, or
// GNU's when POSIXLY_CORRECT=1.
// - Doesn't expose optopt or opterr.
// - option.flag and longindex are not supported and must be nullptr.
enum {
no_argument = 0,
required_argument = 1,
};
struct option {
const char* name;
int has_arg;
std::nullptr_t flag; // Only nullptr is supported.
int val;
};
extern char* optarg;
extern int optind;
extern int optopt;
extern int opterr;
int getopt_long(int argc,
char** argv,
const char* shortopts,
const option* longopts,
std::nullptr_t /*longindex is not supported*/);
int getopt(int argc, char** argv, const char* shortopts);
} // namespace getopt_compat
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_GETOPT_COMPAT_H_

View file

@ -0,0 +1,137 @@
/*
* Copyright (C) 2019 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 INCLUDE_PERFETTO_EXT_BASE_HASH_H_
#define INCLUDE_PERFETTO_EXT_BASE_HASH_H_
#include <stddef.h>
#include <stdint.h>
#include <string>
#include <type_traits>
#include <utility>
namespace perfetto {
namespace base {
// A helper class which computes a 64-bit hash of the input data.
// The algorithm used is FNV-1a as it is fast and easy to implement and has
// relatively few collisions.
// WARNING: This hash function should not be used for any cryptographic purpose.
class Hasher {
public:
// Creates an empty hash object
Hasher() {}
// Hashes a numeric value.
template <
typename T,
typename std::enable_if<std::is_arithmetic<T>::value, bool>::type = true>
void Update(T data) {
Update(reinterpret_cast<const char*>(&data), sizeof(data));
}
// Using the loop instead of "Update(str, strlen(str))" to avoid looping twice
void Update(const char* str) {
for (const auto* p = str; *p; ++p)
Update(*p);
}
// Hashes a byte array.
void Update(const char* data, size_t size) {
for (size_t i = 0; i < size; i++) {
result_ ^= static_cast<uint8_t>(data[i]);
// Note: Arithmetic overflow of unsigned integers is well defined in C++
// standard unlike signed integers.
// https://stackoverflow.com/a/41280273
result_ *= kFnv1a64Prime;
}
}
// Allow hashing anything that has a |data| field, a |size| field,
// and has the kHashable trait (e.g., base::StringView).
template <typename T, typename = std::enable_if<T::kHashable>>
void Update(const T& t) {
Update(t.data(), t.size());
}
void Update(const std::string& s) { Update(s.data(), s.size()); }
uint64_t digest() const { return result_; }
// Usage:
// uint64_t hashed_value = Hash::Combine(33, false, "ABC", 458L, 3u, 'x');
template <typename... Ts>
static uint64_t Combine(Ts&&... args) {
Hasher hasher;
hasher.UpdateAll(std::forward<Ts>(args)...);
return hasher.digest();
}
// `hasher.UpdateAll(33, false, "ABC")` is shorthand for:
// `hasher.Update(33); hasher.Update(false); hasher.Update("ABC");`
void UpdateAll() {}
template <typename T, typename... Ts>
void UpdateAll(T&& arg, Ts&&... args) {
Update(arg);
UpdateAll(std::forward<Ts>(args)...);
}
private:
static constexpr uint64_t kFnv1a64OffsetBasis = 0xcbf29ce484222325;
static constexpr uint64_t kFnv1a64Prime = 0x100000001b3;
uint64_t result_ = kFnv1a64OffsetBasis;
};
// This is for using already-hashed key into std::unordered_map and avoid the
// cost of re-hashing. Example:
// unordered_map<uint64_t, Value, AlreadyHashed> my_map.
template <typename T>
struct AlreadyHashed {
size_t operator()(const T& x) const { return static_cast<size_t>(x); }
};
// base::Hash uses base::Hasher for integer values and falls base to std::hash
// for other types. This is needed as std::hash for integers is just the
// identity function and Perfetto uses open-addressing hash table, which are
// very sensitive to hash quality and are known to degrade in performance
// when using std::hash.
template <typename T>
struct Hash {
// Version for ints, using base::Hasher.
template <typename U = T>
auto operator()(const U& x) ->
typename std::enable_if<std::is_arithmetic<U>::value, size_t>::type
const {
Hasher hash;
hash.Update(x);
return static_cast<size_t>(hash.digest());
}
// Version for non-ints, falling back to std::hash.
template <typename U = T>
auto operator()(const U& x) ->
typename std::enable_if<!std::is_arithmetic<U>::value, size_t>::type
const {
return std::hash<U>()(x);
}
};
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_HASH_H_

View file

@ -0,0 +1,21 @@
# Copyright (C) 2019 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.
source_set("http") {
sources = [
"http_server.h",
"sha1.h",
]
public_deps = [ "..:base" ]
}

View file

@ -0,0 +1,189 @@
/*
* Copyright (C) 2021 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 INCLUDE_PERFETTO_EXT_BASE_HTTP_HTTP_SERVER_H_
#define INCLUDE_PERFETTO_EXT_BASE_HTTP_HTTP_SERVER_H_
#include <array>
#include <initializer_list>
#include <list>
#include <memory>
#include <optional>
#include <string>
#include "perfetto/base/task_runner.h"
#include "perfetto/ext/base/paged_memory.h"
#include "perfetto/ext/base/string_view.h"
#include "perfetto/ext/base/unix_socket.h"
namespace perfetto {
namespace base {
class HttpServerConnection;
struct HttpRequest {
explicit HttpRequest(HttpServerConnection* c) : conn(c) {}
std::optional<StringView> GetHeader(StringView name) const;
HttpServerConnection* conn;
// These StringViews point to memory in the rxbuf owned by |conn|. They are
// valid only within the OnHttpRequest() call.
StringView method;
StringView uri;
StringView origin;
StringView body;
bool is_websocket_handshake = false;
private:
friend class HttpServer;
struct Header {
StringView name;
StringView value;
};
static constexpr uint32_t kMaxHeaders = 32;
std::array<Header, kMaxHeaders> headers{};
size_t num_headers = 0;
};
struct WebsocketMessage {
explicit WebsocketMessage(HttpServerConnection* c) : conn(c) {}
HttpServerConnection* conn;
// Note: message boundaries are not respected in case of fragmentation.
// This websocket implementation preserves only the byte stream, but not the
// atomicity of inbound messages (like SOCK_STREAM, unlike SOCK_DGRAM).
// Holds onto the connection's |rxbuf|. This is valid only within the scope
// of the OnWebsocketMessage() callback.
StringView data;
// If false the payload contains binary data. If true it's supposed to contain
// text. Note that there is no guarantee this will be the case. This merely
// reflect the opcode that the client sets on each message.
bool is_text = false;
};
class HttpServerConnection {
public:
static constexpr size_t kOmitContentLength = static_cast<size_t>(-1);
explicit HttpServerConnection(std::unique_ptr<UnixSocket>);
~HttpServerConnection();
void SendResponseHeaders(const char* http_code,
std::initializer_list<const char*> headers = {},
size_t content_length = 0);
// Works also for websockets.
void SendResponseBody(const void* content, size_t content_length);
void Close();
// All the above in one shot.
void SendResponse(const char* http_code,
std::initializer_list<const char*> headers = {},
StringView content = {},
bool force_close = false);
void SendResponseAndClose(const char* http_code,
std::initializer_list<const char*> headers = {},
StringView content = {}) {
SendResponse(http_code, headers, content, true);
}
// The metods below are only valid for websocket connections.
// Upgrade an existing connection to a websocket. This can be called only in
// the context of OnHttpRequest(req) if req.is_websocket_handshake == true.
// If the origin is not in the |allowed_origins_|, the request will fail with
// a 403 error (this is because there is no browser-side CORS support for
// websockets).
void UpgradeToWebsocket(const HttpRequest&);
void SendWebsocketMessage(const void* data, size_t len);
void SendWebsocketMessage(StringView sv) {
SendWebsocketMessage(sv.data(), sv.size());
}
void SendWebsocketFrame(uint8_t opcode,
const void* payload,
size_t payload_len);
bool is_websocket() const { return is_websocket_; }
private:
friend class HttpServer;
size_t rxbuf_avail() { return rxbuf.size() - rxbuf_used; }
std::unique_ptr<UnixSocket> sock;
PagedMemory rxbuf;
size_t rxbuf_used = 0;
bool is_websocket_ = false;
bool headers_sent_ = false;
size_t content_len_headers_ = 0;
size_t content_len_actual_ = 0;
// If the origin is in the server's |allowed_origins_| this contains the
// origin itself. This is used to handle CORS headers.
std::string origin_allowed_;
// By default treat connections as keep-alive unless the client says
// explicitly 'Connection: close'. This improves TraceProcessor's python API.
// This is consistent with that nginx does.
bool keepalive_ = true;
};
class HttpRequestHandler {
public:
virtual ~HttpRequestHandler();
virtual void OnHttpRequest(const HttpRequest&) = 0;
virtual void OnWebsocketMessage(const WebsocketMessage&);
virtual void OnHttpConnectionClosed(HttpServerConnection*);
};
class HttpServer : public UnixSocket::EventListener {
public:
HttpServer(TaskRunner*, HttpRequestHandler*);
~HttpServer() override;
void Start(int port);
void AddAllowedOrigin(const std::string&);
private:
size_t ParseOneHttpRequest(HttpServerConnection*);
size_t ParseOneWebsocketFrame(HttpServerConnection*);
void HandleCorsPreflightRequest(const HttpRequest&);
bool IsOriginAllowed(StringView);
// UnixSocket::EventListener implementation.
void OnNewIncomingConnection(UnixSocket*,
std::unique_ptr<UnixSocket>) override;
void OnConnect(UnixSocket* self, bool connected) override;
void OnDisconnect(UnixSocket* self) override;
void OnDataAvailable(UnixSocket* self) override;
TaskRunner* const task_runner_;
HttpRequestHandler* req_handler_;
std::unique_ptr<UnixSocket> sock4_;
std::unique_ptr<UnixSocket> sock6_;
std::list<HttpServerConnection> clients_;
std::list<std::string> allowed_origins_;
bool origin_error_logged_ = false;
};
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_HTTP_HTTP_SERVER_H_

View file

@ -0,0 +1,39 @@
/*
* Copyright (C) 2021 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 INCLUDE_PERFETTO_EXT_BASE_HTTP_SHA1_H_
#define INCLUDE_PERFETTO_EXT_BASE_HTTP_SHA1_H_
#include <stddef.h>
#include <array>
#include <cstddef>
#include <cstdint>
#include <string>
namespace perfetto {
namespace base {
constexpr size_t kSHA1Length = 20;
using SHA1Digest = std::array<uint8_t, kSHA1Length>;
SHA1Digest SHA1Hash(const std::string& str);
SHA1Digest SHA1Hash(const void* data, size_t size);
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_HTTP_SHA1_H_

View file

@ -0,0 +1,318 @@
/*
* Copyright (C) 2019 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 INCLUDE_PERFETTO_EXT_BASE_METATRACE_H_
#define INCLUDE_PERFETTO_EXT_BASE_METATRACE_H_
#include <array>
#include <atomic>
#include <functional>
#include <string>
#include "perfetto/base/logging.h"
#include "perfetto/base/thread_utils.h"
#include "perfetto/base/time.h"
#include "perfetto/ext/base/metatrace_events.h"
#include "perfetto/ext/base/utils.h"
// A facility to trace execution of the perfetto codebase itself.
// The meta-tracing framework is organized into three layers:
//
// 1. A static ring-buffer in base/ (this file) that supports concurrent writes
// and a single reader.
// The responsibility of this layer is to store events and counters as
// efficiently as possible without re-entering any tracing code.
// This is really a static-storage-based ring-buffer based on a POD array.
// This layer does NOT deal with serializing the meta-trace buffer.
// It posts a task when it's half full and expects something outside of
// base/ to drain the ring-buffer and serialize it, eventually writing it
// into the trace itself, before it gets 100% full.
//
// 2. A class in tracing/core which takes care of serializing the meta-trace
// buffer into the trace using a TraceWriter. See metatrace_writer.h .
//
// 3. A data source in traced_probes that, when be enabled via the trace config,
// injects metatrace events into the trace. See metatrace_data_source.h .
//
// The available events and tags are defined in metatrace_events.h .
namespace perfetto {
namespace base {
class TaskRunner;
} // namespace base
namespace metatrace {
// Meta-tracing is organized in "tags" that can be selectively enabled. This is
// to enable meta-tracing only of one sub-system. This word has one "enabled"
// bit for each tag. 0 -> meta-tracing off.
extern std::atomic<uint32_t> g_enabled_tags;
// Time of the Enable() call. Used as a reference for keeping delta timestmaps
// in Record.
extern std::atomic<uint64_t> g_enabled_timestamp;
// Enables meta-tracing for one or more tags. Once enabled it will discard any
// further Enable() calls and return false until disabled,
// |read_task| is a closure that will be called enqueued |task_runner| when the
// meta-tracing ring buffer is half full. The task is expected to read the ring
// buffer using RingBuffer::GetReadIterator() and serialize the contents onto a
// file or into the trace itself.
// Must be called on the |task_runner| passed.
// |task_runner| must have static lifetime.
bool Enable(std::function<void()> read_task, base::TaskRunner*, uint32_t tags);
// Disables meta-tracing.
// Must be called on the same |task_runner| as Enable().
void Disable();
inline uint64_t TraceTimeNowNs() {
return static_cast<uint64_t>(base::GetBootTimeNs().count());
}
// Returns a relaxed view of whether metatracing is enabled for the given tag.
// Useful for skipping unnecessary argument computation if metatracing is off.
inline bool IsEnabled(uint32_t tag) {
auto enabled_tags = g_enabled_tags.load(std::memory_order_relaxed);
return PERFETTO_UNLIKELY((enabled_tags & tag) != 0);
}
// Holds the data for a metatrace event or counter.
struct Record {
static constexpr uint16_t kTypeMask = 0x8000;
static constexpr uint16_t kTypeCounter = 0x8000;
static constexpr uint16_t kTypeEvent = 0;
uint64_t timestamp_ns() const {
auto base_ns = g_enabled_timestamp.load(std::memory_order_relaxed);
PERFETTO_DCHECK(base_ns);
return base_ns + ((static_cast<uint64_t>(timestamp_ns_high) << 32) |
timestamp_ns_low);
}
void set_timestamp(uint64_t ts) {
auto t_start = g_enabled_timestamp.load(std::memory_order_relaxed);
uint64_t diff = ts - t_start;
PERFETTO_DCHECK(diff < (1ull << 48));
timestamp_ns_low = static_cast<uint32_t>(diff);
timestamp_ns_high = static_cast<uint16_t>(diff >> 32);
}
// We can't just memset() this class because on MSVC std::atomic<> is not
// trivially constructible anymore. Also std::atomic<> has a deleted copy
// constructor so we cant just do "*this = Record()" either.
// See http://bit.ly/339Jlzd .
void clear() {
this->~Record();
new (this) Record();
}
// This field holds the type (counter vs event) in the MSB and event ID (as
// defined in metatrace_events.h) in the lowest 15 bits. It is also used also
// as a linearization point: this is always written after all the other
// fields with a release-store. This is so the reader can determine whether it
// can safely process the other event fields after a load-acquire.
std::atomic<uint16_t> type_and_id{};
// Timestamp is stored as a 48-bits value diffed against g_enabled_timestamp.
// This gives us 78 hours from Enabled().
uint16_t timestamp_ns_high = 0;
uint32_t timestamp_ns_low = 0;
uint32_t thread_id = 0;
union {
// Only one of the two elements can be zero initialized, clang complains
// about "initializing multiple members of union" otherwise.
uint32_t duration_ns = 0; // If type == event.
int32_t counter_value; // If type == counter.
};
};
// Hold the meta-tracing data into a statically allocated array.
// This class uses static storage (as opposite to being a singleton) to:
// - Have the guarantee of always valid storage, so that meta-tracing can be
// safely used in any part of the codebase, including base/ itself.
// - Avoid barriers that thread-safe static locals would require.
class RingBuffer {
public:
static constexpr size_t kCapacity = 4096; // 4096 * 16 bytes = 64K.
// This iterator is not idempotent and will bump the read index in the buffer
// at the end of the reads. There can be only one reader at any time.
// Usage: for (auto it = RingBuffer::GetReadIterator(); it; ++it) { it->... }
class ReadIterator {
public:
ReadIterator(ReadIterator&& other) {
PERFETTO_DCHECK(other.valid_);
cur_ = other.cur_;
end_ = other.end_;
valid_ = other.valid_;
other.valid_ = false;
}
~ReadIterator() {
if (!valid_)
return;
PERFETTO_DCHECK(cur_ >= RingBuffer::rd_index_);
PERFETTO_DCHECK(cur_ <= RingBuffer::wr_index_);
RingBuffer::rd_index_.store(cur_, std::memory_order_release);
}
explicit operator bool() const { return cur_ < end_; }
const Record* operator->() const { return RingBuffer::At(cur_); }
const Record& operator*() const { return *operator->(); }
// This is for ++it. it++ is deliberately not supported.
ReadIterator& operator++() {
PERFETTO_DCHECK(cur_ < end_);
// Once a record has been read, mark it as free clearing its type_and_id,
// so if we encounter it in another read iteration while being written
// we know it's not fully written yet.
// The memory_order_relaxed below is enough because:
// - The reader is single-threaded and doesn't re-read the same records.
// - Before starting a read batch, the reader has an acquire barrier on
// |rd_index_|.
// - After terminating a read batch, the ~ReadIterator dtor updates the
// |rd_index_| with a release-store.
// - Reader and writer are typically kCapacity/2 apart. So unless an
// overrun happens a writer won't reuse a newly released record any time
// soon. If an overrun happens, everything is busted regardless.
At(cur_)->type_and_id.store(0, std::memory_order_relaxed);
++cur_;
return *this;
}
private:
friend class RingBuffer;
ReadIterator(uint64_t begin, uint64_t end)
: cur_(begin), end_(end), valid_(true) {}
ReadIterator& operator=(const ReadIterator&) = delete;
ReadIterator(const ReadIterator&) = delete;
uint64_t cur_;
uint64_t end_;
bool valid_;
};
static Record* At(uint64_t index) {
// Doesn't really have to be pow2, but if not the compiler will emit
// arithmetic operations to compute the modulo instead of a bitwise AND.
static_assert(!(kCapacity & (kCapacity - 1)), "kCapacity must be pow2");
PERFETTO_DCHECK(index >= rd_index_);
PERFETTO_DCHECK(index <= wr_index_);
return &records_[index % kCapacity];
}
// Must be called on the same task runner passed to Enable()
static ReadIterator GetReadIterator() {
PERFETTO_DCHECK(RingBuffer::IsOnValidTaskRunner());
return ReadIterator(rd_index_.load(std::memory_order_acquire),
wr_index_.load(std::memory_order_acquire));
}
static Record* AppendNewRecord();
static void Reset();
static bool has_overruns() {
return has_overruns_.load(std::memory_order_acquire);
}
// Can temporarily return a value >= kCapacity but is eventually consistent.
// This would happen in case of overruns until threads hit the --wr_index_
// in AppendNewRecord().
static uint64_t GetSizeForTesting() {
auto wr_index = wr_index_.load(std::memory_order_relaxed);
auto rd_index = rd_index_.load(std::memory_order_relaxed);
PERFETTO_DCHECK(wr_index >= rd_index);
return wr_index - rd_index;
}
private:
friend class ReadIterator;
// Returns true if the caller is on the task runner passed to Enable().
// Used only for DCHECKs.
static bool IsOnValidTaskRunner();
static std::array<Record, kCapacity> records_;
static std::atomic<bool> read_task_queued_;
static std::atomic<uint64_t> wr_index_;
static std::atomic<uint64_t> rd_index_;
static std::atomic<bool> has_overruns_;
static Record bankruptcy_record_; // Used in case of overruns.
};
inline void TraceCounter(uint32_t tag, uint16_t id, int32_t value) {
// memory_order_relaxed is okay because the storage has static lifetime.
// It is safe to accidentally log an event soon after disabling.
auto enabled_tags = g_enabled_tags.load(std::memory_order_relaxed);
if (PERFETTO_LIKELY((enabled_tags & tag) == 0))
return;
Record* record = RingBuffer::AppendNewRecord();
record->thread_id = static_cast<uint32_t>(base::GetThreadId());
record->set_timestamp(TraceTimeNowNs());
record->counter_value = value;
record->type_and_id.store(Record::kTypeCounter | id,
std::memory_order_release);
}
class ScopedEvent {
public:
ScopedEvent(uint32_t tag, uint16_t event_id) {
auto enabled_tags = g_enabled_tags.load(std::memory_order_relaxed);
if (PERFETTO_LIKELY((enabled_tags & tag) == 0))
return;
event_id_ = event_id;
record_ = RingBuffer::AppendNewRecord();
record_->thread_id = static_cast<uint32_t>(base::GetThreadId());
record_->set_timestamp(TraceTimeNowNs());
}
~ScopedEvent() {
if (PERFETTO_LIKELY(!record_))
return;
auto now = TraceTimeNowNs();
record_->duration_ns = static_cast<uint32_t>(now - record_->timestamp_ns());
record_->type_and_id.store(Record::kTypeEvent | event_id_,
std::memory_order_release);
}
private:
Record* record_ = nullptr;
uint16_t event_id_ = 0;
ScopedEvent(const ScopedEvent&) = delete;
ScopedEvent& operator=(const ScopedEvent&) = delete;
};
// Boilerplate to derive a unique variable name for the event.
#define PERFETTO_METATRACE_UID2(a, b) a##b
#define PERFETTO_METATRACE_UID(x) PERFETTO_METATRACE_UID2(metatrace_, x)
#define PERFETTO_METATRACE_SCOPED(TAG, ID) \
::perfetto::metatrace::ScopedEvent PERFETTO_METATRACE_UID(__COUNTER__)( \
::perfetto::metatrace::TAG, ::perfetto::metatrace::ID)
#define PERFETTO_METATRACE_COUNTER(TAG, ID, VALUE) \
::perfetto::metatrace::TraceCounter(::perfetto::metatrace::TAG, \
::perfetto::metatrace::ID, \
static_cast<int32_t>(VALUE))
} // namespace metatrace
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_METATRACE_H_

View file

@ -0,0 +1,117 @@
/*
* Copyright (C) 2019 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 INCLUDE_PERFETTO_EXT_BASE_METATRACE_EVENTS_H_
#define INCLUDE_PERFETTO_EXT_BASE_METATRACE_EVENTS_H_
#include <stdint.h>
namespace perfetto {
namespace metatrace {
enum Tags : uint32_t {
TAG_NONE = 0,
TAG_ANY = uint32_t(-1),
TAG_FTRACE = 1 << 0,
TAG_PROC_POLLERS = 1 << 1,
TAG_TRACE_WRITER = 1 << 2,
TAG_TRACE_SERVICE = 1 << 3,
TAG_PRODUCER = 1 << 4,
};
// The macros below generate matching enums and arrays of string literals.
// This is to avoid maintaining string maps manually.
// clang-format off
// DO NOT remove or reshuffle items in this list, only append. The ID of these
// events are an ABI, the trace processor relies on these to open old traces.
#define PERFETTO_METATRACE_EVENTS(F) \
F(EVENT_ZERO_UNUSED), \
F(FTRACE_CPU_READER_READ), /*unused*/ \
F(FTRACE_DRAIN_CPUS), /*unused*/ \
F(FTRACE_UNBLOCK_READERS), /*unused*/ \
F(FTRACE_CPU_READ_NONBLOCK), /*unused*/ \
F(FTRACE_CPU_READ_BLOCK), /*unused*/ \
F(FTRACE_CPU_SPLICE_NONBLOCK), /*unused*/ \
F(FTRACE_CPU_SPLICE_BLOCK), /*unused*/ \
F(FTRACE_CPU_WAIT_CMD), /*unused*/ \
F(FTRACE_CPU_RUN_CYCLE), /*unused*/ \
F(FTRACE_CPU_FLUSH), \
F(FTRACE_CPU_BUFFER_WATERMARK), \
F(READ_SYS_STATS), \
F(PS_WRITE_ALL_PROCESSES), \
F(PS_ON_PIDS), \
F(PS_ON_RENAME_PIDS), \
F(PS_WRITE_ALL_PROCESS_STATS), \
F(TRACE_WRITER_COMMIT_STARTUP_WRITER_BATCH), \
F(FTRACE_READ_TICK), \
F(FTRACE_CPU_READ_CYCLE), \
F(FTRACE_CPU_READ_BATCH), \
F(KALLSYMS_PARSE), \
F(PROFILER_READ_TICK), \
F(PROFILER_READ_CPU), \
F(PROFILER_UNWIND_TICK), \
F(PROFILER_UNWIND_SAMPLE), \
F(PROFILER_UNWIND_INITIAL_ATTEMPT), \
F(PROFILER_UNWIND_ATTEMPT), \
F(PROFILER_MAPS_PARSE), \
F(PROFILER_MAPS_REPARSE), \
F(PROFILER_UNWIND_CACHE_CLEAR)
// Append only, see above.
//
// Values that aren't used as counters:
// * FTRACE_SERVICE_COMMIT_DATA is a bit-packed representation of an event, see
// tracing_service_impl.cc for the format.
// * PROFILER_UNWIND_CURRENT_PID represents the PID that is being unwound.
//
#define PERFETTO_METATRACE_COUNTERS(F) \
F(COUNTER_ZERO_UNUSED),\
F(FTRACE_PAGES_DRAINED), \
F(PS_PIDS_SCANNED), \
F(TRACE_SERVICE_COMMIT_DATA), \
F(PROFILER_UNWIND_QUEUE_SZ), \
F(PROFILER_UNWIND_CURRENT_PID)
// clang-format on
#define PERFETTO_METATRACE_IDENTITY(name) name
#define PERFETTO_METATRACE_TOSTRING(name) #name
enum Events : uint16_t {
PERFETTO_METATRACE_EVENTS(PERFETTO_METATRACE_IDENTITY),
EVENTS_MAX
};
constexpr char const* kEventNames[] = {
PERFETTO_METATRACE_EVENTS(PERFETTO_METATRACE_TOSTRING)};
enum Counters : uint16_t {
PERFETTO_METATRACE_COUNTERS(PERFETTO_METATRACE_IDENTITY),
COUNTERS_MAX
};
constexpr char const* kCounterNames[] = {
PERFETTO_METATRACE_COUNTERS(PERFETTO_METATRACE_TOSTRING)};
inline void SuppressUnusedVarsInAmalgamatedBuild() {
(void)kCounterNames;
(void)kEventNames;
}
} // namespace metatrace
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_METATRACE_EVENTS_H_

View file

@ -0,0 +1,78 @@
/*
* Copyright (C) 2019 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 INCLUDE_PERFETTO_EXT_BASE_NO_DESTRUCTOR_H_
#define INCLUDE_PERFETTO_EXT_BASE_NO_DESTRUCTOR_H_
#include <new>
#include <utility>
namespace perfetto {
namespace base {
// Wrapper that can hold an object of type T, without invoking the contained
// object's destructor when being destroyed. Useful for creating statics while
// avoiding static destructors.
//
// Stores the object inline, and therefore doesn't incur memory allocation and
// pointer indirection overheads.
//
// Example of use:
//
// const std::string& GetStr() {
// static base::NoDestructor<std::string> s("hello");
// return s.ref();
// }
//
template <typename T>
class NoDestructor {
public:
// Forward arguments to T's constructor. Note that this doesn't cover
// construction from initializer lists.
template <typename... Args>
explicit NoDestructor(Args&&... args) {
new (storage_) T(std::forward<Args>(args)...);
}
NoDestructor(const NoDestructor&) = delete;
NoDestructor& operator=(const NoDestructor&) = delete;
NoDestructor(NoDestructor&&) = delete;
NoDestructor& operator=(NoDestructor&&) = delete;
~NoDestructor() = default;
/* To avoid type-punned pointer strict aliasing warnings on GCC6 and below
* these need to be split over two lines. If they are collapsed onto one line.
* return reinterpret_cast<const T*>(storage_);
* The error fires.
*/
const T& ref() const {
auto* const cast = reinterpret_cast<const T*>(storage_);
return *cast;
}
T& ref() {
auto* const cast = reinterpret_cast<T*>(storage_);
return *cast;
}
private:
alignas(T) char storage_[sizeof(T)];
};
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_NO_DESTRUCTOR_H_

View file

@ -0,0 +1,105 @@
/*
* Copyright (C) 2017 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 INCLUDE_PERFETTO_EXT_BASE_PAGED_MEMORY_H_
#define INCLUDE_PERFETTO_EXT_BASE_PAGED_MEMORY_H_
#include <cstddef>
#include <memory>
#include "perfetto/base/build_config.h"
#include "perfetto/ext/base/container_annotations.h"
// We need to track the committed size on windows and when ASAN is enabled.
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) || defined(ADDRESS_SANITIZER)
#define TRACK_COMMITTED_SIZE() 1
#else
#define TRACK_COMMITTED_SIZE() 0
#endif
namespace perfetto {
namespace base {
class PagedMemory {
public:
// Initializes an invalid PagedMemory pointing to nullptr.
PagedMemory();
~PagedMemory();
PagedMemory(PagedMemory&& other) noexcept;
PagedMemory& operator=(PagedMemory&& other);
enum AllocationFlags {
// By default, Allocate() crashes if the underlying mmap fails (e.g., if out
// of virtual address space). When this flag is provided, an invalid
// PagedMemory pointing to nullptr is returned in this case instead.
kMayFail = 1 << 0,
// By default, Allocate() commits the allocated memory immediately. When
// this flag is provided, the memory virtual address space may only be
// reserved and the user should call EnsureCommitted() before writing to
// memory addresses.
kDontCommit = 1 << 1,
};
// Allocates |size| bytes using mmap(MAP_ANONYMOUS). The returned memory is
// guaranteed to be page-aligned and guaranteed to be zeroed.
// For |flags|, see the AllocationFlags enum above.
static PagedMemory Allocate(size_t size, int flags = 0);
// Hint to the OS that the memory range is not needed and can be discarded.
// The memory remains accessible and its contents may be retained, or they
// may be zeroed. This function may be a NOP on some platforms. Returns true
// if implemented.
bool AdviseDontNeed(void* p, size_t size);
// Ensures that at least the first |committed_size| bytes of the allocated
// memory region are committed. The implementation may commit memory in larger
// chunks above |committed_size|. Crashes if the memory couldn't be committed.
#if TRACK_COMMITTED_SIZE()
void EnsureCommitted(size_t committed_size);
#else // TRACK_COMMITTED_SIZE()
void EnsureCommitted(size_t /*committed_size*/) {}
#endif // TRACK_COMMITTED_SIZE()
inline void* Get() const noexcept { return p_; }
inline bool IsValid() const noexcept { return !!p_; }
inline size_t size() const noexcept { return size_; }
private:
PagedMemory(char* p, size_t size);
PagedMemory(const PagedMemory&) = delete;
// Defaulted for implementation of move constructor + assignment.
PagedMemory& operator=(const PagedMemory&) = default;
char* p_ = nullptr;
// The size originally passed to Allocate(). The actual virtual memory
// reservation will be larger due to: (i) guard pages; (ii) rounding up to
// the system page size.
size_t size_ = 0;
#if TRACK_COMMITTED_SIZE()
size_t committed_size_ = 0u;
#endif // TRACK_COMMITTED_SIZE()
};
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_PAGED_MEMORY_H_

View file

@ -0,0 +1,90 @@
/*
* Copyright (C) 2021 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 INCLUDE_PERFETTO_EXT_BASE_PERIODIC_TASK_H_
#define INCLUDE_PERFETTO_EXT_BASE_PERIODIC_TASK_H_
#include <functional>
#include "perfetto/ext/base/scoped_file.h"
#include "perfetto/ext/base/thread_checker.h"
#include "perfetto/ext/base/weak_ptr.h"
namespace perfetto {
namespace base {
class TaskRunner;
// A periodic task utility class. It wraps the logic necessary to do periodic
// tasks using a TaskRunner, taking care of subtleties like ensuring that
// outstanding tasks are cancelled after reset/dtor.
// Tasks are aligned on wall time (unless they are |one_shot|). This is to
// ensure that when using multiple periodic tasks, they happen at the same time,
// minimizing context switches.
// On Linux/Android it also supports suspend-aware mode (via timerfd). On other
// operating systems it falls back to PostDelayedTask, which is not
// suspend-aware.
// TODO(primiano): this should probably become a periodic timer scheduler, so we
// can use one FD for everything rather than one FD per task. For now we take
// the hit of a FD-per-task to keep this low-risk.
// TODO(primiano): consider renaming this class to TimerTask. When |one_shot|
// is set, the "Periodic" part of the class name becomes a lie.
class PeriodicTask {
public:
explicit PeriodicTask(base::TaskRunner*);
~PeriodicTask(); // Calls Reset().
struct Args {
uint32_t period_ms = 0;
std::function<void()> task = nullptr;
bool start_first_task_immediately = false;
bool use_suspend_aware_timer = false;
bool one_shot = false;
};
void Start(Args);
// Safe to be called multiple times, even without calling Start():
void Reset();
// No copy or move. WeakPtr-wrapped pointers to |this| are posted on the
// task runner, this class is not easily movable.
PeriodicTask(const PeriodicTask&) = delete;
PeriodicTask& operator=(const PeriodicTask&) = delete;
PeriodicTask(PeriodicTask&&) = delete;
PeriodicTask& operator=(PeriodicTask&&) = delete;
base::PlatformHandle timer_fd_for_testing() { return *timer_fd_; }
private:
static void RunTaskAndPostNext(base::WeakPtr<PeriodicTask>,
uint32_t generation);
void PostNextTask();
void ResetTimerFd();
base::TaskRunner* const task_runner_;
Args args_;
uint32_t generation_ = 0;
base::ScopedPlatformHandle timer_fd_;
PERFETTO_THREAD_CHECKER(thread_checker_)
base::WeakPtrFactory<PeriodicTask> weak_ptr_factory_; // Keep last.
};
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_PERIODIC_TASK_H_

View file

@ -0,0 +1,50 @@
/*
* Copyright (C) 2018 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 INCLUDE_PERFETTO_EXT_BASE_PIPE_H_
#define INCLUDE_PERFETTO_EXT_BASE_PIPE_H_
#include "perfetto/base/platform_handle.h"
#include "perfetto/ext/base/scoped_file.h"
namespace perfetto {
namespace base {
class Pipe {
public:
enum Flags {
kBothBlock = 0,
#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
kBothNonBlock,
kRdNonBlock,
kWrNonBlock,
#endif
};
static Pipe Create(Flags = kBothBlock);
Pipe();
Pipe(Pipe&&) noexcept;
Pipe& operator=(Pipe&&);
ScopedPlatformHandle rd;
ScopedPlatformHandle wr;
};
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_PIPE_H_

View file

@ -0,0 +1,40 @@
/*
* Copyright (C) 2023 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 INCLUDE_PERFETTO_EXT_BASE_PLATFORM_H_
#define INCLUDE_PERFETTO_EXT_BASE_PLATFORM_H_
namespace perfetto {
namespace base {
namespace platform {
// Executed before entering a syscall (e.g. poll, read, write etc) which might
// block.
// This is overridden in Google internal builds for dealing with userspace
// scheduling.
void BeforeMaybeBlockingSyscall();
// Executed after entering a syscall (e.g. poll, read, write etc) which might
// block.
// This is overridden in Google internal builds for dealing with userspace
// scheduling.
void AfterMaybeBlockingSyscall();
} // namespace platform
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_PLATFORM_H_

View file

@ -0,0 +1,122 @@
/*
* Copyright (C) 2017 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 INCLUDE_PERFETTO_EXT_BASE_SCOPED_FILE_H_
#define INCLUDE_PERFETTO_EXT_BASE_SCOPED_FILE_H_
#include "perfetto/base/build_config.h"
#include <stdio.h>
#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
#include <dirent.h> // For DIR* / opendir().
#endif
#include <string>
#include "perfetto/base/export.h"
#include "perfetto/base/logging.h"
#include "perfetto/base/platform_handle.h"
namespace perfetto {
namespace base {
namespace internal {
// Used for the most common cases of ScopedResource where there is only one
// invalid value.
template <typename T, T InvalidValue>
struct DefaultValidityChecker {
static bool IsValid(T t) { return t != InvalidValue; }
};
} // namespace internal
// RAII classes for auto-releasing fds and dirs.
// if T is a pointer type, InvalidValue must be nullptr. Doing otherwise
// causes weird unexpected behaviors (See https://godbolt.org/z/5nGMW4).
template <typename T,
int (*CloseFunction)(T),
T InvalidValue,
bool CheckClose = true,
class Checker = internal::DefaultValidityChecker<T, InvalidValue>>
class ScopedResource {
public:
using ValidityChecker = Checker;
static constexpr T kInvalid = InvalidValue;
explicit ScopedResource(T t = InvalidValue) : t_(t) {}
ScopedResource(ScopedResource&& other) noexcept {
t_ = other.t_;
other.t_ = InvalidValue;
}
ScopedResource& operator=(ScopedResource&& other) {
reset(other.t_);
other.t_ = InvalidValue;
return *this;
}
T get() const { return t_; }
T operator*() const { return t_; }
explicit operator bool() const { return Checker::IsValid(t_); }
void reset(T r = InvalidValue) {
if (Checker::IsValid(t_)) {
int res = CloseFunction(t_);
if (CheckClose)
PERFETTO_CHECK(res == 0);
}
t_ = r;
}
T release() {
T t = t_;
t_ = InvalidValue;
return t;
}
~ScopedResource() { reset(InvalidValue); }
private:
ScopedResource(const ScopedResource&) = delete;
ScopedResource& operator=(const ScopedResource&) = delete;
T t_;
};
// Declared in file_utils.h. Forward declared to avoid #include cycles.
int PERFETTO_EXPORT_COMPONENT CloseFile(int fd);
// Use this for file resources obtained via open() and similar APIs.
using ScopedFile = ScopedResource<int, CloseFile, -1>;
using ScopedFstream = ScopedResource<FILE*, fclose, nullptr>;
// Use this for resources that are HANDLE on Windows. See comments in
// platform_handle.h
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
using ScopedPlatformHandle = ScopedResource<PlatformHandle,
ClosePlatformHandle,
/*InvalidValue=*/nullptr,
/*CheckClose=*/true,
PlatformHandleChecker>;
#else
// On non-windows systems we alias ScopedPlatformHandle to ScopedFile because
// they are really the same. This is to allow assignments between the two in
// Linux-specific code paths that predate ScopedPlatformHandle.
static_assert(std::is_same<int, PlatformHandle>::value, "");
using ScopedPlatformHandle = ScopedFile;
// DIR* does not exist on Windows.
using ScopedDir = ScopedResource<DIR*, closedir, nullptr>;
#endif
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_SCOPED_FILE_H_

View file

@ -0,0 +1,92 @@
/*
* 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.
*/
#ifndef INCLUDE_PERFETTO_EXT_BASE_SCOPED_MMAP_H_
#define INCLUDE_PERFETTO_EXT_BASE_SCOPED_MMAP_H_
#include <cstddef>
#include "perfetto/base/build_config.h"
#include "perfetto/ext/base/scoped_file.h"
#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) || \
PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
#define PERFETTO_HAS_MMAP() 1
#else
#define PERFETTO_HAS_MMAP() 0
#endif
namespace perfetto::base {
// RAII wrapper that holds ownership of an mmap()d area and of a file. Calls
// unmap() and close() on destruction.
class ScopedMmap {
public:
// Creates a memory mapping for the first `length` bytes of `file`.
static ScopedMmap FromHandle(base::ScopedPlatformHandle file, size_t length);
ScopedMmap() {}
~ScopedMmap();
ScopedMmap(ScopedMmap&& other) noexcept;
ScopedMmap& operator=(ScopedMmap&& other) noexcept;
// Returns a pointer to the mapped memory area. Only valid if `IsValid()` is
// true.
void* data() const { return ptr_; }
// Returns true if this object contains a successfully mapped area.
bool IsValid() const { return ptr_ != nullptr; }
// Returns the length of the mapped area.
size_t length() const { return length_; }
// Unmaps the area and closes the file. Returns false if this held a mmap()d
// area and unmapping failed. In any case, after this method, `IsValid()` will
// return false.
bool reset() noexcept;
#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
// Takes ownership of an mmap()d area that starts at `data`, `size` bytes
// long. `data` should not be MAP_FAILED.
static ScopedMmap InheritMmappedRange(void* data, size_t size);
#endif
private:
ScopedMmap(const ScopedMmap&) = delete;
ScopedMmap& operator=(const ScopedMmap&) = delete;
size_t length_ = 0;
void* ptr_ = nullptr;
ScopedPlatformHandle file_;
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
ScopedPlatformHandle map_;
#endif
};
// Tries to open `fname` and maps its first `length` bytes in memory.
ScopedMmap ReadMmapFilePart(const char* fname, size_t length);
// Tries to open `fname` and maps the whole file into memory.
ScopedMmap ReadMmapWholeFile(const char* fname);
} // namespace perfetto::base
#endif // INCLUDE_PERFETTO_EXT_BASE_SCOPED_MMAP_H_

View file

@ -0,0 +1,62 @@
/*
* Copyright (C) 2018 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 INCLUDE_PERFETTO_EXT_BASE_SMALL_SET_H_
#define INCLUDE_PERFETTO_EXT_BASE_SMALL_SET_H_
#include <array>
namespace perfetto {
// Set that can store up to Size items of DataType.
// Lookup is O(Size), so it is only usable for very small sets.
template <typename DataType, size_t Size>
class SmallSet {
static_assert(Size < 16, "Do not use SmallSet for many items");
public:
// Name for consistency with STL.
using const_iterator = typename std::array<DataType, Size>::const_iterator;
bool Add(DataType n) {
if (Contains(n))
return true;
if (filled_ < Size) {
arr_[filled_++] = std::move(n);
return true;
}
return false;
}
bool Contains(const DataType& n) const {
for (size_t i = 0; i < filled_; ++i) {
if (arr_[i] == n)
return true;
}
return false;
}
const_iterator begin() const { return arr_.cbegin(); }
const_iterator end() const { return arr_.cbegin() + filled_; }
size_t size() const { return filled_; }
private:
std::array<DataType, Size> arr_;
size_t filled_ = 0;
};
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_SMALL_SET_H_

View file

@ -0,0 +1,197 @@
/*
* Copyright (C) 2021 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 INCLUDE_PERFETTO_EXT_BASE_SMALL_VECTOR_H_
#define INCLUDE_PERFETTO_EXT_BASE_SMALL_VECTOR_H_
#include <algorithm>
#include <type_traits>
#include <utility>
#include "perfetto/base/compiler.h"
#include "perfetto/base/logging.h"
#include "perfetto/ext/base/utils.h"
namespace perfetto {
namespace base {
// Uses inline storage first, switches to dynamic storage when it overflows.
template <typename T, size_t kSize>
class SmallVector {
public:
static constexpr size_t kInlineSize = kSize;
explicit SmallVector() = default;
~SmallVector() {
clear();
if (PERFETTO_UNLIKELY(is_using_heap()))
AlignedFree(begin_);
begin_ = end_ = end_of_storage_ = nullptr;
}
// Move operators.
SmallVector(SmallVector&& other) noexcept(
std::is_nothrow_move_constructible<T>::value) {
if (other.is_using_heap()) {
// Move the heap content, no need to move the individual objects as their
// location won't change.
begin_ = other.begin_;
end_ = other.end_;
end_of_storage_ = other.end_of_storage_;
} else {
const size_t other_size = other.size();
PERFETTO_DCHECK(other_size <= capacity());
for (size_t i = 0; i < other_size; i++) {
// Move the entries and destroy the ones in the moved-from object.
new (&begin_[i]) T(std::move(other.begin_[i]));
other.begin_[i].~T();
}
end_ = begin_ + other_size;
}
auto* const other_inline_storage = other.inline_storage_begin();
other.end_ = other.begin_ = other_inline_storage;
other.end_of_storage_ = other_inline_storage + kInlineSize;
}
SmallVector& operator=(SmallVector&& other) noexcept(
std::is_nothrow_move_constructible<T>::value) {
this->~SmallVector();
new (this) SmallVector<T, kSize>(std::move(other));
return *this;
}
// Copy operators.
SmallVector(const SmallVector& other) {
const size_t other_size = other.size();
if (other_size > capacity())
Grow(other_size);
// Copy-construct the elements.
for (size_t i = 0; i < other_size; ++i)
new (&begin_[i]) T(other.begin_[i]);
end_ = begin_ + other_size;
}
SmallVector& operator=(const SmallVector& other) {
if (PERFETTO_UNLIKELY(this == &other))
return *this;
this->~SmallVector();
new (this) SmallVector<T, kSize>(other);
return *this;
}
T* data() { return begin_; }
const T* data() const { return begin_; }
T* begin() { return begin_; }
const T* begin() const { return begin_; }
T* end() { return end_; }
const T* end() const { return end_; }
size_t size() const { return static_cast<size_t>(end_ - begin_); }
bool empty() const { return end_ == begin_; }
size_t capacity() const {
return static_cast<size_t>(end_of_storage_ - begin_);
}
T& front() {
PERFETTO_DCHECK(!empty());
return begin_[0];
}
const T& front() const {
PERFETTO_DCHECK(!empty());
return begin_[0];
}
T& back() {
PERFETTO_DCHECK(!empty());
return end_[-1];
}
const T& back() const {
PERFETTO_DCHECK(!empty());
return end_[-1];
}
T& operator[](size_t index) {
PERFETTO_DCHECK(index < size());
return begin_[index];
}
const T& operator[](size_t index) const {
PERFETTO_DCHECK(index < size());
return begin_[index];
}
template <typename... Args>
void emplace_back(Args&&... args) {
T* end = end_;
if (PERFETTO_UNLIKELY(end == end_of_storage_))
end = Grow();
new (end) T(std::forward<Args>(args)...);
end_ = end + 1;
}
void pop_back() {
PERFETTO_DCHECK(!empty());
back().~T();
--end_;
}
// Clear without reverting back to inline storage.
void clear() {
while (!empty())
pop_back();
}
private:
PERFETTO_NO_INLINE T* Grow(size_t desired_capacity = 0) {
size_t cur_size = size();
size_t new_capacity = desired_capacity;
if (desired_capacity <= cur_size)
new_capacity = std::max(capacity() * 2, size_t(128));
T* new_storage =
static_cast<T*>(AlignedAlloc(alignof(T), new_capacity * sizeof(T)));
for (size_t i = 0; i < cur_size; ++i) {
// Move the elements into the new heap buffer and destroy the old ones.
new (&new_storage[i]) T(std::move(begin_[i]));
begin_[i].~T();
}
if (is_using_heap())
AlignedFree(begin_);
begin_ = new_storage;
end_ = new_storage + cur_size;
end_of_storage_ = new_storage + new_capacity;
return end_;
}
T* inline_storage_begin() { return reinterpret_cast<T*>(&inline_storage_); }
bool is_using_heap() { return begin_ != inline_storage_begin(); }
T* begin_ = inline_storage_begin();
T* end_ = begin_;
T* end_of_storage_ = begin_ + kInlineSize;
typename std::aligned_storage<sizeof(T) * kInlineSize, alignof(T)>::type
inline_storage_;
};
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_SMALL_VECTOR_H_

View file

@ -0,0 +1,83 @@
/*
* Copyright (C) 2022 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 INCLUDE_PERFETTO_EXT_BASE_STATUS_OR_H_
#define INCLUDE_PERFETTO_EXT_BASE_STATUS_OR_H_
#include <optional>
#include "perfetto/base/status.h"
namespace perfetto {
namespace base {
// Union of a object of type |T| with a |base::Status|. Useful for cases where
// a |T| indicates a successful result of an operation and |base::Status|
// represents an error.
//
// This class is modelled closely on absl::Status and should essentially 1:1
// match it's API.
template <typename T>
class StatusOr {
public:
// Matches naming of declarations in similar types e.g. std::optional,
// std::variant.
using value_type = T;
// Intentionally implicit to allow idomatic usage (e.g. returning value/status
// from base::StatusOr returning function).
StatusOr(base::Status status) : StatusOr(std::move(status), std::nullopt) {
if (status.ok()) {
// Matches what Abseil's approach towards OkStatus being passed to
// absl::StatusOr<T>.
PERFETTO_FATAL("base::OkStatus passed to StatusOr: this is not allowd");
}
}
StatusOr(T value) : StatusOr(base::OkStatus(), std::move(value)) {}
bool ok() const { return status_.ok(); }
const base::Status& status() const { return status_; }
T& value() {
PERFETTO_DCHECK(status_.ok());
return *value_;
}
const T& value() const { return *value_; }
T& operator*() { return value(); }
const T& operator*() const { return value(); }
T* operator->() { return &value(); }
const T* operator->() const { return &value(); }
private:
StatusOr(base::Status status, std::optional<T> value)
: status_(std::move(status)), value_(std::move(value)) {
PERFETTO_DCHECK(!status_.ok() || value_.has_value());
}
base::Status status_;
std::optional<T> value_;
};
// Deduction guide to make returning StatusOr less verbose.
template <typename T>
StatusOr(T) -> StatusOr<T>;
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_STATUS_OR_H_

View file

@ -0,0 +1,90 @@
/*
* Copyright (C) 2018 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 INCLUDE_PERFETTO_EXT_BASE_STRING_SPLITTER_H_
#define INCLUDE_PERFETTO_EXT_BASE_STRING_SPLITTER_H_
#include <string>
namespace perfetto {
namespace base {
// C++ version of strtok(). Splits a string without making copies or any heap
// allocations. Destructs the original string passed in input.
// Supports the special case of using \0 as a delimiter.
// The token returned in output are valid as long as the input string is valid.
class StringSplitter {
public:
// Whether an empty string (two delimiters side-to-side) is a valid token.
enum class EmptyTokenMode {
DISALLOW_EMPTY_TOKENS,
ALLOW_EMPTY_TOKENS,
DEFAULT = DISALLOW_EMPTY_TOKENS,
};
// Can take ownership of the string if passed via std::move(), e.g.:
// StringSplitter(std::move(str), '\n');
StringSplitter(std::string,
char delimiter,
EmptyTokenMode empty_token_mode = EmptyTokenMode::DEFAULT);
// Splits a C-string. The input string will be forcefully null-terminated (so
// str[size - 1] should be == '\0' or the last char will be truncated).
StringSplitter(char* str,
size_t size,
char delimiter,
EmptyTokenMode empty_token_mode = EmptyTokenMode::DEFAULT);
// Splits the current token from an outer StringSplitter instance. This is to
// chain splitters as follows:
// for (base::StringSplitter lines(x, '\n'); ss.Next();)
// for (base::StringSplitter words(&lines, ' '); words.Next();)
StringSplitter(StringSplitter*,
char delimiter,
EmptyTokenMode empty_token_mode = EmptyTokenMode::DEFAULT);
// Returns true if a token is found (in which case it will be stored in
// cur_token()), false if no more tokens are found.
bool Next();
// Returns the current token iff last call to Next() returned true. In this
// case it guarantees that the returned string is always null terminated.
// In all other cases (before the 1st call to Next() and after Next() returns
// false) returns nullptr.
char* cur_token() { return cur_; }
// Returns the length of the current token (excluding the null terminator).
size_t cur_token_size() const { return cur_size_; }
private:
StringSplitter(const StringSplitter&) = delete;
StringSplitter& operator=(const StringSplitter&) = delete;
void Initialize(char* str, size_t size);
std::string str_;
char* cur_;
size_t cur_size_;
char* next_;
char* end_; // STL-style, points one past the last char.
const char delimiter_;
const EmptyTokenMode empty_token_mode_;
};
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_STRING_SPLITTER_H_

View file

@ -0,0 +1,233 @@
/*
* Copyright (C) 2018 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 INCLUDE_PERFETTO_EXT_BASE_STRING_UTILS_H_
#define INCLUDE_PERFETTO_EXT_BASE_STRING_UTILS_H_
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <cinttypes>
#include <optional>
#include <string>
#include <vector>
#include "perfetto/ext/base/string_view.h"
namespace perfetto {
namespace base {
inline char Lowercase(char c) {
return ('A' <= c && c <= 'Z') ? static_cast<char>(c - ('A' - 'a')) : c;
}
inline char Uppercase(char c) {
return ('a' <= c && c <= 'z') ? static_cast<char>(c + ('A' - 'a')) : c;
}
inline std::optional<uint32_t> CStringToUInt32(const char* s, int base = 10) {
char* endptr = nullptr;
auto value = static_cast<uint32_t>(strtoul(s, &endptr, base));
return (*s && !*endptr) ? std::make_optional(value) : std::nullopt;
}
inline std::optional<int32_t> CStringToInt32(const char* s, int base = 10) {
char* endptr = nullptr;
auto value = static_cast<int32_t>(strtol(s, &endptr, base));
return (*s && !*endptr) ? std::make_optional(value) : std::nullopt;
}
// Note: it saturates to 7fffffffffffffff if parsing a hex number >= 0x8000...
inline std::optional<int64_t> CStringToInt64(const char* s, int base = 10) {
char* endptr = nullptr;
auto value = static_cast<int64_t>(strtoll(s, &endptr, base));
return (*s && !*endptr) ? std::make_optional(value) : std::nullopt;
}
inline std::optional<uint64_t> CStringToUInt64(const char* s, int base = 10) {
char* endptr = nullptr;
auto value = static_cast<uint64_t>(strtoull(s, &endptr, base));
return (*s && !*endptr) ? std::make_optional(value) : std::nullopt;
}
double StrToD(const char* nptr, char** endptr);
inline std::optional<double> CStringToDouble(const char* s) {
char* endptr = nullptr;
double value = StrToD(s, &endptr);
std::optional<double> result(std::nullopt);
if (*s != '\0' && *endptr == '\0')
result = value;
return result;
}
inline std::optional<uint32_t> StringToUInt32(const std::string& s,
int base = 10) {
return CStringToUInt32(s.c_str(), base);
}
inline std::optional<int32_t> StringToInt32(const std::string& s,
int base = 10) {
return CStringToInt32(s.c_str(), base);
}
inline std::optional<uint64_t> StringToUInt64(const std::string& s,
int base = 10) {
return CStringToUInt64(s.c_str(), base);
}
inline std::optional<int64_t> StringToInt64(const std::string& s,
int base = 10) {
return CStringToInt64(s.c_str(), base);
}
inline std::optional<double> StringToDouble(const std::string& s) {
return CStringToDouble(s.c_str());
}
bool StartsWith(const std::string& str, const std::string& prefix);
bool EndsWith(const std::string& str, const std::string& suffix);
bool StartsWithAny(const std::string& str,
const std::vector<std::string>& prefixes);
bool Contains(const std::string& haystack, const std::string& needle);
bool Contains(const std::string& haystack, char needle);
size_t Find(const StringView& needle, const StringView& haystack);
bool CaseInsensitiveEqual(const std::string& first, const std::string& second);
std::string Join(const std::vector<std::string>& parts,
const std::string& delim);
std::vector<std::string> SplitString(const std::string& text,
const std::string& delimiter);
std::string StripPrefix(const std::string& str, const std::string& prefix);
std::string StripSuffix(const std::string& str, const std::string& suffix);
std::string TrimWhitespace(const std::string& str);
std::string ToLower(const std::string& str);
std::string ToUpper(const std::string& str);
std::string StripChars(const std::string& str,
const std::string& chars,
char replacement);
std::string ToHex(const char* data, size_t size);
inline std::string ToHex(const std::string& s) {
return ToHex(s.c_str(), s.size());
}
std::string IntToHexString(uint32_t number);
std::string Uint64ToHexString(uint64_t number);
std::string Uint64ToHexStringNoPrefix(uint64_t number);
std::string ReplaceAll(std::string str,
const std::string& to_replace,
const std::string& replacement);
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
bool WideToUTF8(const std::wstring& source, std::string& output);
bool UTF8ToWide(const std::string& source, std::wstring& output);
#endif // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
// A BSD-style strlcpy without the return value.
// Copies at most |dst_size|-1 characters. Unlike strncpy, it always \0
// terminates |dst|, as long as |dst_size| is not 0.
// Unlike strncpy and like strlcpy it does not zero-pad the rest of |dst|.
// Returns nothing. The BSD strlcpy returns the size of |src|, which might
// be > |dst_size|. Anecdotal experience suggests people assume the return value
// is the number of bytes written in |dst|. That assumption can lead to
// dangerous bugs.
// In order to avoid being subtly uncompliant with strlcpy AND avoid misuse,
// the choice here is to return nothing.
inline void StringCopy(char* dst, const char* src, size_t dst_size) {
for (size_t i = 0; i < dst_size; ++i) {
if ((dst[i] = src[i]) == '\0') {
return; // We hit and copied the null terminator.
}
}
// We were left off at dst_size. We over copied 1 byte. Null terminate.
if (PERFETTO_LIKELY(dst_size > 0))
dst[dst_size - 1] = 0;
}
// Like snprintf() but returns the number of chars *actually* written (without
// counting the null terminator) NOT "the number of chars which would have been
// written to the final string if enough space had been available".
// This should be used in almost all cases when the caller uses the return value
// of snprintf(). If the return value is not used, there is no benefit in using
// this wrapper, as this just calls snprintf() and mangles the return value.
// It always null-terminates |dst| (even in case of errors), unless
// |dst_size| == 0.
// Examples:
// SprintfTrunc(x, 4, "123whatever"): returns 3 and writes "123\0".
// SprintfTrunc(x, 4, "123"): returns 3 and writes "123\0".
// SprintfTrunc(x, 3, "123"): returns 2 and writes "12\0".
// SprintfTrunc(x, 2, "123"): returns 1 and writes "1\0".
// SprintfTrunc(x, 1, "123"): returns 0 and writes "\0".
// SprintfTrunc(x, 0, "123"): returns 0 and writes nothing.
// NOTE: This means that the caller has no way to tell when truncation happens
// vs the edge case of *just* fitting in the buffer.
size_t SprintfTrunc(char* dst, size_t dst_size, const char* fmt, ...)
PERFETTO_PRINTF_FORMAT(3, 4);
// Line number starts from 1
struct LineWithOffset {
base::StringView line;
uint32_t line_offset;
uint32_t line_num;
};
// For given string and offset Pfinds a line with character for
// which offset points, what number is this line (starts from 1), and the offset
// inside this line. returns std::nullopt if the offset points to
// line break character or exceeds string length.
std::optional<LineWithOffset> FindLineWithOffset(base::StringView str,
uint32_t offset);
// A helper class to facilitate construction and usage of write-once stack
// strings.
// Example usage:
// StackString<32> x("format %d %s", 42, string_arg);
// TakeString(x.c_str() | x.string_view() | x.ToStdString());
// Rather than char x[32] + sprintf.
// Advantages:
// - Avoids useless zero-fills caused by people doing `char buf[32] {}` (mainly
// by fearing unknown snprintf failure modes).
// - Makes the code more robust in case of snprintf truncations (len() and
// string_view() will return the truncated length, unlike snprintf).
template <size_t N>
class StackString {
public:
explicit PERFETTO_PRINTF_FORMAT(/* 1=this */ 2, 3)
StackString(const char* fmt, ...) {
buf_[0] = '\0';
va_list args;
va_start(args, fmt);
int res = vsnprintf(buf_, sizeof(buf_), fmt, args);
va_end(args);
buf_[sizeof(buf_) - 1] = '\0';
len_ = res < 0 ? 0 : std::min(static_cast<size_t>(res), sizeof(buf_) - 1);
}
StringView string_view() const { return StringView(buf_, len_); }
std::string ToStdString() const { return std::string(buf_, len_); }
const char* c_str() const { return buf_; }
size_t len() const { return len_; }
char* mutable_data() { return buf_; }
private:
char buf_[N];
size_t len_ = 0; // Does not include the \0.
};
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_STRING_UTILS_H_

View file

@ -0,0 +1,201 @@
/*
* Copyright (C) 2018 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 INCLUDE_PERFETTO_EXT_BASE_STRING_VIEW_H_
#define INCLUDE_PERFETTO_EXT_BASE_STRING_VIEW_H_
#include <string.h>
#include <algorithm>
#include <string>
#include "perfetto/base/build_config.h"
#include "perfetto/base/logging.h"
#include "perfetto/ext/base/hash.h"
namespace perfetto {
namespace base {
// A string-like object that refers to a non-owned piece of memory.
// Strings are internally NOT null terminated.
class StringView {
public:
// Allow hashing with base::Hash.
static constexpr bool kHashable = true;
static constexpr size_t npos = static_cast<size_t>(-1);
StringView() : data_(nullptr), size_(0) {}
StringView(const StringView&) = default;
StringView& operator=(const StringView&) = default;
StringView(const char* data, size_t size) : data_(data), size_(size) {
PERFETTO_DCHECK(size == 0 || data != nullptr);
}
// Allow implicit conversion from any class that has a |data| and |size| field
// and has the kConvertibleToStringView trait (e.g., protozero::ConstChars).
template <typename T, typename = std::enable_if<T::kConvertibleToStringView>>
StringView(const T& x) : StringView(x.data, x.size) {
PERFETTO_DCHECK(x.size == 0 || x.data != nullptr);
}
// Creates a StringView from a null-terminated C string.
// Deliberately not "explicit".
StringView(const char* cstr) : data_(cstr), size_(strlen(cstr)) {
PERFETTO_DCHECK(cstr != nullptr);
}
// This instead has to be explicit, as creating a StringView out of a
// std::string can be subtle.
explicit StringView(const std::string& str)
: data_(str.data()), size_(str.size()) {}
bool empty() const { return size_ == 0; }
size_t size() const { return size_; }
const char* data() const { return data_; }
const char* begin() const { return data_; }
const char* end() const { return data_ + size_; }
char at(size_t pos) const {
PERFETTO_DCHECK(pos < size_);
return data_[pos];
}
size_t find(char c, size_t start_pos = 0) const {
for (size_t i = start_pos; i < size_; ++i) {
if (data_[i] == c)
return i;
}
return npos;
}
size_t find(const StringView& str, size_t start_pos = 0) const {
if (start_pos > size())
return npos;
auto it = std::search(begin() + start_pos, end(), str.begin(), str.end());
size_t pos = static_cast<size_t>(it - begin());
return pos + str.size() <= size() ? pos : npos;
}
size_t find(const char* str, size_t start_pos = 0) const {
return find(StringView(str), start_pos);
}
size_t rfind(char c) const {
for (size_t i = size_; i > 0; --i) {
if (data_[i - 1] == c)
return i - 1;
}
return npos;
}
StringView substr(size_t pos, size_t count = npos) const {
if (pos >= size_)
return StringView("", 0);
size_t rcount = std::min(count, size_ - pos);
return StringView(data_ + pos, rcount);
}
bool CaseInsensitiveEq(const StringView& other) const {
if (size() != other.size())
return false;
if (size() == 0)
return true;
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
return _strnicmp(data(), other.data(), size()) == 0;
#else
return strncasecmp(data(), other.data(), size()) == 0;
#endif
}
bool StartsWith(const StringView& other) const {
if (other.size() == 0)
return true;
if (size() == 0)
return false;
if (other.size() > size())
return false;
return memcmp(data(), other.data(), other.size()) == 0;
}
bool EndsWith(const StringView& other) const {
if (other.size() == 0)
return true;
if (size() == 0)
return false;
if (other.size() > size())
return false;
size_t off = size() - other.size();
return memcmp(data() + off, other.data(), other.size()) == 0;
}
std::string ToStdString() const {
return size_ == 0 ? "" : std::string(data_, size_);
}
uint64_t Hash() const {
base::Hasher hasher;
hasher.Update(data_, size_);
return hasher.digest();
}
private:
const char* data_ = nullptr;
size_t size_ = 0;
};
inline bool operator==(const StringView& x, const StringView& y) {
if (x.size() != y.size())
return false;
if (x.size() == 0)
return true;
return memcmp(x.data(), y.data(), x.size()) == 0;
}
inline bool operator!=(const StringView& x, const StringView& y) {
return !(x == y);
}
inline bool operator<(const StringView& x, const StringView& y) {
auto size = std::min(x.size(), y.size());
if (size == 0)
return x.size() < y.size();
int result = memcmp(x.data(), y.data(), size);
return result < 0 || (result == 0 && x.size() < y.size());
}
inline bool operator>=(const StringView& x, const StringView& y) {
return !(x < y);
}
inline bool operator>(const StringView& x, const StringView& y) {
return y < x;
}
inline bool operator<=(const StringView& x, const StringView& y) {
return !(y < x);
}
} // namespace base
} // namespace perfetto
template <>
struct std::hash<::perfetto::base::StringView> {
size_t operator()(const ::perfetto::base::StringView& sv) const {
return static_cast<size_t>(sv.Hash());
}
};
#endif // INCLUDE_PERFETTO_EXT_BASE_STRING_VIEW_H_

View file

@ -0,0 +1,183 @@
/*
* Copyright (C) 2019 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 INCLUDE_PERFETTO_EXT_BASE_STRING_WRITER_H_
#define INCLUDE_PERFETTO_EXT_BASE_STRING_WRITER_H_
#include <string.h>
#include <cinttypes>
#include <cmath>
#include <cstdlib>
#include <limits>
#include "perfetto/base/logging.h"
#include "perfetto/ext/base/string_utils.h"
#include "perfetto/ext/base/string_view.h"
namespace perfetto {
namespace base {
// A helper class which writes formatted data to a string buffer.
// This is used in the trace processor where we write O(GBs) of strings and
// sprintf is too slow.
class StringWriter {
public:
// Creates a string buffer from a char buffer and length.
StringWriter(char* buffer, size_t size) : buffer_(buffer), size_(size) {}
// Appends n instances of a char to the buffer.
void AppendChar(char in, size_t n = 1) {
PERFETTO_DCHECK(pos_ + n <= size_);
memset(&buffer_[pos_], in, n);
pos_ += n;
}
// Appends a length delimited string to the buffer.
void AppendString(const char* in, size_t n) {
PERFETTO_DCHECK(pos_ + n <= size_);
memcpy(&buffer_[pos_], in, n);
pos_ += n;
}
void AppendStringView(StringView sv) { AppendString(sv.data(), sv.size()); }
// Appends a null-terminated string literal to the buffer.
template <size_t N>
inline void AppendLiteral(const char (&in)[N]) {
AppendString(in, N - 1);
}
// Appends a StringView to the buffer.
void AppendString(StringView data) { AppendString(data.data(), data.size()); }
// Appends an integer to the buffer.
void AppendInt(int64_t value) { AppendPaddedInt<'0', 0>(value); }
// Appends an integer to the buffer, padding with |padchar| if the number of
// digits of the integer is less than |padding|.
template <char padchar, uint64_t padding>
void AppendPaddedInt(int64_t sign_value) {
const bool negate = std::signbit(static_cast<double>(sign_value));
uint64_t absolute_value;
if (sign_value == std::numeric_limits<int64_t>::min()) {
absolute_value =
static_cast<uint64_t>(std::numeric_limits<int64_t>::max()) + 1;
} else {
absolute_value = static_cast<uint64_t>(std::abs(sign_value));
}
AppendPaddedInt<padchar, padding>(absolute_value, negate);
}
void AppendUnsignedInt(uint64_t value) {
AppendPaddedUnsignedInt<'0', 0>(value);
}
// Appends an unsigned integer to the buffer, padding with |padchar| if the
// number of digits of the integer is less than |padding|.
template <char padchar, uint64_t padding>
void AppendPaddedUnsignedInt(uint64_t value) {
AppendPaddedInt<padchar, padding>(value, false);
}
// Appends a hex integer to the buffer.
template <typename IntType>
void AppendHexInt(IntType value) {
// TODO(lalitm): trying to optimize this is premature given we almost never
// print hex ints. Reevaluate this in the future if we do print them more.
size_t res =
base::SprintfTrunc(buffer_ + pos_, size_ - pos_, "%" PRIx64, value);
PERFETTO_DCHECK(pos_ + res <= size_);
pos_ += res;
}
// Appends a double to the buffer.
void AppendDouble(double value) {
// TODO(lalitm): trying to optimize this is premature given we almost never
// print doubles. Reevaluate this in the future if we do print them more.
size_t res = base::SprintfTrunc(buffer_ + pos_, size_ - pos_, "%lf", value);
PERFETTO_DCHECK(pos_ + res <= size_);
pos_ += res;
}
void AppendBool(bool value) {
if (value) {
AppendLiteral("true");
return;
}
AppendLiteral("false");
}
StringView GetStringView() {
PERFETTO_DCHECK(pos_ <= size_);
return StringView(buffer_, pos_);
}
char* CreateStringCopy() {
char* dup = reinterpret_cast<char*>(malloc(pos_ + 1));
if (dup) {
memcpy(dup, buffer_, pos_);
dup[pos_] = '\0';
}
return dup;
}
size_t pos() const { return pos_; }
size_t size() const { return size_; }
void reset() { pos_ = 0; }
private:
template <char padchar, uint64_t padding>
void AppendPaddedInt(uint64_t absolute_value, bool negate) {
// Need to add 2 to the number of digits to account for minus sign and
// rounding down of digits10.
constexpr auto kMaxDigits = std::numeric_limits<uint64_t>::digits10 + 2;
constexpr auto kSizeNeeded = kMaxDigits > padding ? kMaxDigits : padding;
PERFETTO_DCHECK(pos_ + kSizeNeeded <= size_);
char data[kSizeNeeded];
size_t idx;
for (idx = kSizeNeeded - 1; absolute_value >= 10;) {
char digit = absolute_value % 10;
absolute_value /= 10;
data[idx--] = digit + '0';
}
data[idx--] = static_cast<char>(absolute_value) + '0';
if (padding > 0) {
size_t num_digits = kSizeNeeded - 1 - idx;
// std::max() needed to work around GCC not being able to tell that
// padding > 0.
for (size_t i = num_digits; i < std::max(uint64_t{1u}, padding); i++) {
data[idx--] = padchar;
}
}
if (negate)
buffer_[pos_++] = '-';
AppendString(&data[idx + 1], kSizeNeeded - idx - 1);
}
char* buffer_ = nullptr;
size_t size_ = 0;
size_t pos_ = 0;
};
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_STRING_WRITER_H_

View file

@ -0,0 +1,281 @@
/*
* 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 INCLUDE_PERFETTO_EXT_BASE_SUBPROCESS_H_
#define INCLUDE_PERFETTO_EXT_BASE_SUBPROCESS_H_
#include <condition_variable>
#include <functional>
#include <initializer_list>
#include <mutex>
#include <optional>
#include <string>
#include <thread>
#include <vector>
#include "perfetto/base/build_config.h"
#include "perfetto/base/logging.h"
#include "perfetto/base/platform_handle.h"
#include "perfetto/base/proc_utils.h"
#include "perfetto/ext/base/event_fd.h"
#include "perfetto/ext/base/pipe.h"
#include "perfetto/ext/base/scoped_file.h"
namespace perfetto {
namespace base {
// Handles creation and lifecycle management of subprocesses, taking care of
// all subtleties involved in handling processes on UNIX.
// This class allows to deal with macro two use-cases:
// 1) fork() + exec() equivalent: for spawning a brand new process image.
// This happens when |args.exec_cmd| is not empty.
// This is safe to use even in a multi-threaded environment.
// 2) fork(): for spawning a process and running a function.
// This happens when |args.posix_entrypoint_for_testing| is not empty.
// This is intended only for tests as it is extremely subtle.
// This mode must be used with extreme care. Before the entrypoint is
// invoked all file descriptors other than stdin/out/err and the ones
// specified in |args.preserve_fds| will be closed, to avoid each process
// retaining a dupe of other subprocesses pipes. This however means that
// any non trivial calls (including logging) must be avoided as they might
// refer to FDs that are now closed. The entrypoint should really be used
// just to signal a pipe or similar for synchronizing sequencing in tests.
//
// This class allows to control stdin/out/err pipe redirection and takes care
// of keeping all the pipes pumped (stdin) / drained (stdout/err), in a similar
// fashion of python's subprocess.Communicate()
// stdin: is always piped and closed once the |args.input| buffer is written.
// stdout/err can be either:
// - dup()ed onto the parent process stdout/err.
// - redirected onto /dev/null.
// - piped onto a buffer (see output() method). There is only one output
// buffer in total. If both stdout and stderr are set to kBuffer mode, they
// will be merged onto the same. There doesn't seem any use case where they
// are needed distinctly.
//
// Some caveats worth mentioning:
// - It always waitpid()s, to avoid leaving zombies around. If the process is
// not terminated by the time the destructor is reached, the dtor will
// send a SIGKILL and wait for the termination.
// - After fork()-ing it will close all file descriptors, preserving only
// stdin/out/err and the fds listed in |args.preserve_fds|.
// - On Linux/Android, the child process will be SIGKILL-ed if the calling
// thread exists, even if the Subprocess is std::move()-d onto another thread.
// This happens by virtue PR_SET_PDEATHSIG, which is used to avoid that
// child processes are leaked in the case of a crash of the parent (frequent
// in tests). However, the child process might still be leaked if execing
// a setuid/setgid binary (see man 2 prctl).
//
// Usage:
// base::Subprocess p({"/bin/cat", "-"});
// (or equivalently:
// base::Subprocess p;
// p.args.exec_cmd.push_back("/bin/cat");
// p.args.exec_cmd.push_back("-");
// )
// p.args.stdout_mode = base::Subprocess::kBuffer;
// p.args.stderr_mode = base::Subprocess::kInherit;
// p.args.input = "stdin contents";
// p.Call();
// (or equivalently:
// p.Start();
// p.Wait();
// )
// EXPECT_EQ(p.status(), base::Subprocess::kTerminated);
// EXPECT_EQ(p.returncode(), 0);
class Subprocess {
public:
enum Status {
kNotStarted = 0, // Before calling Start() or Call().
kRunning, // After calling Start(), before Wait().
kTerminated, // The subprocess terminated, either successfully or not.
// This includes crashes or other signals on UNIX.
};
enum class OutputMode {
kInherit = 0, // Inherit's the caller process stdout/stderr.
kDevNull, // dup() onto /dev/null.
kBuffer, // dup() onto a pipe and move it into the output() buffer.
kFd, // dup() onto the passed args.fd.
};
enum class InputMode {
kBuffer = 0, // dup() onto a pipe and write args.input on it.
kDevNull, // dup() onto /dev/null.
};
// Input arguments for configuring the subprocess behavior.
struct Args {
Args(std::initializer_list<std::string> _cmd = {}) : exec_cmd(_cmd) {}
Args(Args&&) noexcept;
Args& operator=(Args&&);
// If non-empty this will cause an exec() when Start()/Call() are called.
std::vector<std::string> exec_cmd;
#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
// If non-empty, it changes the argv[0] argument passed to exec. If
// unset, argv[0] == exec_cmd[0]. This is to handle cases like:
// exec_cmd = {"/proc/self/exec"}, argv0: "my_custom_test_override".
std::string posix_argv0_override_for_testing;
// If non-empty this will be invoked on the fork()-ed child process, after
// stdin/out/err has been redirected and all other file descriptor are
// closed. It is valid to specify both |exec_cmd| AND
// |posix_entrypoint_for_testing|. In this case the latter will be invoked
// just before the exec() call, but after having closed all fds % stdin/o/e.
// This is for synchronization barriers in tests.
std::function<void()> posix_entrypoint_for_testing;
// When set, will will move the process to the given process group. If set
// and zero, it will create a new process group. Effectively this calls
// setpgid(0 /*self_pid*/, posix_proc_group_id).
// This can be used to avoid that subprocesses receive CTRL-C from the
// terminal, while still living in the same session.
std::optional<pid_t> posix_proc_group_id{};
#endif
// If non-empty, replaces the environment passed to exec().
std::vector<std::string> env;
// The file descriptors in this list will not be closed.
std::vector<int> preserve_fds;
// The data to push in the child process stdin, if input_mode ==
// InputMode::kBuffer.
std::string input;
InputMode stdin_mode = InputMode::kBuffer;
OutputMode stdout_mode = OutputMode::kInherit;
OutputMode stderr_mode = OutputMode::kInherit;
base::ScopedPlatformHandle out_fd;
// Returns " ".join(exec_cmd), quoting arguments.
std::string GetCmdString() const;
};
struct ResourceUsage {
uint32_t cpu_utime_ms = 0;
uint32_t cpu_stime_ms = 0;
uint32_t max_rss_kb = 0;
uint32_t min_page_faults = 0;
uint32_t maj_page_faults = 0;
uint32_t vol_ctx_switch = 0;
uint32_t invol_ctx_switch = 0;
uint32_t cpu_time_ms() const { return cpu_utime_ms + cpu_stime_ms; }
};
explicit Subprocess(std::initializer_list<std::string> exec_cmd = {});
Subprocess(Subprocess&&) noexcept;
Subprocess& operator=(Subprocess&&);
~Subprocess(); // It will KillAndWaitForTermination() if still alive.
// Starts the subprocess but doesn't wait for its termination. The caller
// is expected to either call Wait() or Poll() after this call.
void Start();
// Wait for process termination. Can be called more than once.
// Args:
// |timeout_ms| = 0: wait indefinitely.
// |timeout_ms| > 0: wait for at most |timeout_ms|.
// Returns:
// True: The process terminated. See status() and returncode().
// False: Timeout reached, the process is still running. In this case the
// process will be left in the kRunning state.
bool Wait(int timeout_ms = 0);
// Equivalent of Start() + Wait();
// Returns true if the process exited cleanly with return code 0. False in
// any othe case.
bool Call(int timeout_ms = 0);
Status Poll();
// Sends a signal (SIGKILL if not specified) and wait for process termination.
void KillAndWaitForTermination(int sig_num = 0);
PlatformProcessId pid() const { return s_->pid; }
// The accessors below are updated only after a call to Poll(), Wait() or
// KillAndWaitForTermination().
// In most cases you want to call Poll() rather than these accessors.
Status status() const { return s_->status; }
int returncode() const { return s_->returncode; }
bool timed_out() const { return s_->timed_out; }
// This contains both stdout and stderr (if the corresponding _mode ==
// OutputMode::kBuffer). It's non-const so the caller can std::move() it.
std::string& output() { return s_->output; }
const std::string& output() const { return s_->output; }
const ResourceUsage& posix_rusage() const { return *s_->rusage; }
Args args;
private:
// The signal/exit code used when killing the process in case of a timeout.
static const int kTimeoutSignal;
Subprocess(const Subprocess&) = delete;
Subprocess& operator=(const Subprocess&) = delete;
// This is to deal robustly with the move operators, without having to
// manually maintain member-wise move instructions.
struct MovableState {
base::Pipe stdin_pipe;
base::Pipe stdouterr_pipe;
PlatformProcessId pid;
Status status = kNotStarted;
int returncode = -1;
std::string output; // Stdin+stderr. Only when OutputMode::kBuffer.
std::unique_ptr<ResourceUsage> rusage{new ResourceUsage()};
bool timed_out = false;
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
std::thread stdouterr_thread;
std::thread stdin_thread;
ScopedPlatformHandle win_proc_handle;
ScopedPlatformHandle win_thread_handle;
base::EventFd stdouterr_done_event;
std::mutex mutex; // Protects locked_outerr_buf and the two pipes.
std::string locked_outerr_buf;
#else
base::Pipe exit_status_pipe;
size_t input_written = 0;
std::thread waitpid_thread;
#endif
};
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
static void StdinThread(MovableState*, std::string input);
static void StdoutErrThread(MovableState*);
#else
void TryPushStdin();
void TryReadStdoutAndErr();
void TryReadExitStatus();
bool PollInternal(int poll_timeout_ms);
#endif
std::unique_ptr<MovableState> s_;
};
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_SUBPROCESS_H_

View file

@ -0,0 +1,63 @@
/*
* Copyright (C) 2022 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 INCLUDE_PERFETTO_EXT_BASE_SYS_TYPES_H_
#define INCLUDE_PERFETTO_EXT_BASE_SYS_TYPES_H_
// This headers deals with sys types commonly used in the codebase that are
// missing on Windows.
#include <sys/types.h>
#include <cstdint>
#include "perfetto/base/build_config.h"
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
#if !PERFETTO_BUILDFLAG(PERFETTO_COMPILER_GCC)
// MinGW has these. clang-cl and MSVC, which use just the Windows SDK, don't.
using uid_t = int;
using pid_t = int;
#endif // !GCC
#if defined(_WIN64)
using ssize_t = int64_t;
#else
using ssize_t = long;
#endif // _WIN64
#endif // OS_WIN
#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && !defined(AID_SHELL)
// From libcutils' android_filesystem_config.h .
#define AID_SHELL 2000
#endif
namespace perfetto {
namespace base {
// The machine ID used in the tracing core.
using MachineID = uint32_t;
// The default value reserved for the host trace.
constexpr MachineID kDefaultMachineID = 0;
constexpr uid_t kInvalidUid = static_cast<uid_t>(-1);
constexpr pid_t kInvalidPid = static_cast<pid_t>(-1);
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_SYS_TYPES_H_

View file

@ -0,0 +1,80 @@
/*
* Copyright (C) 2018 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 INCLUDE_PERFETTO_EXT_BASE_TEMP_FILE_H_
#define INCLUDE_PERFETTO_EXT_BASE_TEMP_FILE_H_
#include <string>
#include "perfetto/ext/base/scoped_file.h"
namespace perfetto {
namespace base {
std::string GetSysTempDir();
class TempFile {
public:
static TempFile CreateUnlinked();
static TempFile Create();
TempFile(TempFile&&) noexcept;
TempFile& operator=(TempFile&&);
~TempFile();
const std::string& path() const { return path_; }
int fd() const { return *fd_; }
int operator*() const { return *fd_; }
// Unlinks the file from the filesystem but keeps the fd() open.
// It is safe to call this multiple times.
void Unlink();
// Releases the underlying file descriptor. Will unlink the file from the
// filesystem if it was created via CreateUnlinked().
ScopedFile ReleaseFD();
private:
TempFile();
TempFile(const TempFile&) = delete;
TempFile& operator=(const TempFile&) = delete;
ScopedFile fd_;
std::string path_;
};
class TempDir {
public:
static TempDir Create();
TempDir(TempDir&&) noexcept;
TempDir& operator=(TempDir&&);
~TempDir();
const std::string& path() const { return path_; }
private:
TempDir();
TempDir(const TempDir&) = delete;
TempDir& operator=(const TempDir&) = delete;
std::string path_;
};
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_TEMP_FILE_H_

View file

@ -0,0 +1,38 @@
/*
* Copyright (C) 2019 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 INCLUDE_PERFETTO_EXT_BASE_THREAD_ANNOTATIONS_H_
#define INCLUDE_PERFETTO_EXT_BASE_THREAD_ANNOTATIONS_H_
#include "perfetto/base/build_config.h"
// Windows TSAN doesn't currently support these annotations.
#if defined(THREAD_SANITIZER) && !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
extern "C" {
void AnnotateBenignRaceSized(const char* file,
int line,
const volatile void* address,
size_t size,
const char* description);
}
#define PERFETTO_ANNOTATE_BENIGN_RACE_SIZED(pointer, size, description) \
AnnotateBenignRaceSized(__FILE__, __LINE__, pointer, size, description);
#else // defined(ADDRESS_SANITIZER)
#define PERFETTO_ANNOTATE_BENIGN_RACE_SIZED(pointer, size, description)
#endif // defined(ADDRESS_SANITIZER)
#endif // INCLUDE_PERFETTO_EXT_BASE_THREAD_ANNOTATIONS_H_

View file

@ -0,0 +1,68 @@
/*
* Copyright (C) 2017 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 INCLUDE_PERFETTO_EXT_BASE_THREAD_CHECKER_H_
#define INCLUDE_PERFETTO_EXT_BASE_THREAD_CHECKER_H_
#include "perfetto/base/build_config.h"
#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
#include <pthread.h>
#endif
#include <atomic>
#include "perfetto/base/export.h"
#include "perfetto/base/logging.h"
#include "perfetto/ext/base/utils.h"
namespace perfetto {
namespace base {
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
using ThreadID = unsigned long;
#else
using ThreadID = pthread_t;
#endif
class PERFETTO_EXPORT_COMPONENT ThreadChecker {
public:
ThreadChecker();
~ThreadChecker();
ThreadChecker(const ThreadChecker&);
ThreadChecker& operator=(const ThreadChecker&);
bool CalledOnValidThread() const PERFETTO_WARN_UNUSED_RESULT;
void DetachFromThread();
private:
mutable std::atomic<ThreadID> thread_id_;
};
#if PERFETTO_DCHECK_IS_ON() && !PERFETTO_BUILDFLAG(PERFETTO_CHROMIUM_BUILD)
// TODO(primiano) Use Chromium's thread checker in Chromium.
#define PERFETTO_THREAD_CHECKER(name) base::ThreadChecker name;
#define PERFETTO_DCHECK_THREAD(name) \
PERFETTO_DCHECK((name).CalledOnValidThread())
#define PERFETTO_DETACH_FROM_THREAD(name) (name).DetachFromThread()
#else
#define PERFETTO_THREAD_CHECKER(name)
#define PERFETTO_DCHECK_THREAD(name)
#define PERFETTO_DETACH_FROM_THREAD(name)
#endif // PERFETTO_DCHECK_IS_ON()
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_THREAD_CHECKER_H_

View file

@ -0,0 +1,85 @@
/*
* Copyright (C) 2019 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 INCLUDE_PERFETTO_EXT_BASE_THREAD_TASK_RUNNER_H_
#define INCLUDE_PERFETTO_EXT_BASE_THREAD_TASK_RUNNER_H_
#include <functional>
#include <thread>
#include "perfetto/ext/base/unix_task_runner.h"
namespace perfetto {
namespace base {
// A UnixTaskRunner backed by a dedicated task thread. Shuts down the runner and
// joins the thread upon destruction. Can be moved to transfer ownership.
//
// Guarantees that:
// * the UnixTaskRunner will be constructed and destructed on the task thread.
// * the task thread will live for the lifetime of the UnixTaskRunner.
//
class PERFETTO_EXPORT_COMPONENT ThreadTaskRunner : public TaskRunner {
public:
static ThreadTaskRunner CreateAndStart(const std::string& name = "") {
return ThreadTaskRunner(name);
}
ThreadTaskRunner(const ThreadTaskRunner&) = delete;
ThreadTaskRunner& operator=(const ThreadTaskRunner&) = delete;
ThreadTaskRunner(ThreadTaskRunner&&) noexcept;
ThreadTaskRunner& operator=(ThreadTaskRunner&&);
~ThreadTaskRunner() override;
// Executes the given function on the task runner thread and blocks the caller
// thread until the function has run.
void PostTaskAndWaitForTesting(std::function<void()>);
// Can be called from another thread to get the CPU time of the thread the
// task-runner is executing on.
uint64_t GetThreadCPUTimeNsForTesting();
// Returns a pointer to the UnixTaskRunner, which is valid for the lifetime of
// this ThreadTaskRunner object (unless this object is moved-from, in which
// case the pointer remains valid for the lifetime of the new owning
// ThreadTaskRunner).
//
// Warning: do not call Quit() on the returned runner pointer, the termination
// should be handled exclusively by this class' destructor.
UnixTaskRunner* get() const { return task_runner_; }
// TaskRunner implementation.
// These methods just proxy to the underlying task_runner_.
void PostTask(std::function<void()>) override;
void PostDelayedTask(std::function<void()>, uint32_t delay_ms) override;
void AddFileDescriptorWatch(PlatformHandle, std::function<void()>) override;
void RemoveFileDescriptorWatch(PlatformHandle) override;
bool RunsTasksOnCurrentThread() const override;
private:
explicit ThreadTaskRunner(const std::string& name);
void RunTaskThread(std::function<void(UnixTaskRunner*)> initializer);
std::thread thread_;
std::string name_;
UnixTaskRunner* task_runner_ = nullptr;
};
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_THREAD_TASK_RUNNER_H_

View file

@ -0,0 +1,90 @@
/*
* 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 INCLUDE_PERFETTO_EXT_BASE_THREAD_UTILS_H_
#define INCLUDE_PERFETTO_EXT_BASE_THREAD_UTILS_H_
#include <string>
#include "perfetto/base/build_config.h"
#include "perfetto/ext/base/string_utils.h"
#include "perfetto/base/export.h"
#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
#include <pthread.h>
#include <string.h>
#include <algorithm>
#endif
#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
#include <sys/prctl.h>
#endif
// Internal implementation utils that aren't as widely useful/supported as
// base/thread_utils.h.
namespace perfetto {
namespace base {
#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
// Sets the "comm" of the calling thread to the first 15 chars of the given
// string.
inline bool MaybeSetThreadName(const std::string& name) {
char buf[16] = {};
StringCopy(buf, name.c_str(), sizeof(buf));
#if PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
return pthread_setname_np(buf) == 0;
#else
return pthread_setname_np(pthread_self(), buf) == 0;
#endif
}
inline bool GetThreadName(std::string& out_result) {
char buf[16] = {};
#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
if (prctl(PR_GET_NAME, buf) != 0)
return false;
#else
if (pthread_getname_np(pthread_self(), buf, sizeof(buf)) != 0)
return false;
#endif
out_result = std::string(buf);
return true;
}
#elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
PERFETTO_EXPORT_COMPONENT bool MaybeSetThreadName(const std::string& name);
PERFETTO_EXPORT_COMPONENT bool GetThreadName(std::string& out_result);
#else
inline bool MaybeSetThreadName(const std::string&) {
return false;
}
inline bool GetThreadName(std::string&) {
return false;
}
#endif
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_THREAD_UTILS_H_

View file

@ -0,0 +1,33 @@
# Copyright (C) 2023 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.
import("../../../../../gn/perfetto.gni")
source_set("threading") {
sources = [
"channel.h",
"future.h",
"future_combinators.h",
"poll.h",
"spawn.h",
"stream.h",
"stream_combinators.h",
"thread_pool.h",
"util.h",
]
deps = [
"..:base",
"../../../../../gn:default_deps",
]
}

View file

@ -0,0 +1,180 @@
/*
* Copyright (C) 2022 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 INCLUDE_PERFETTO_EXT_BASE_THREADING_CHANNEL_H_
#define INCLUDE_PERFETTO_EXT_BASE_THREADING_CHANNEL_H_
#include <mutex>
#include <optional>
#include "perfetto/base/compiler.h"
#include "perfetto/base/platform_handle.h"
#include "perfetto/ext/base/circular_queue.h"
#include "perfetto/ext/base/event_fd.h"
namespace perfetto {
namespace base {
// Unidirectional conduit used to send values between threads with a fixed-sized
// buffer in-between.
//
// When a channel is read from when empty or written to when full, the operation
// will not succeed and the caller can choose to a) abandon the operation,
// or b) use |read_fd| or |write_fd| (as appropriate) which will be become
// "ready" (i.e. base::TaskRunner watches will fire) when the operation would
// succeed.
//
// A channel is very similar to a Unix pipe except with the values being sent
// a) not needing to be serializable b) data does not go through the kernel.
template <typename T>
class Channel {
public:
struct ReadResult {
ReadResult(std::optional<T> _item, bool _is_closed)
: item(std::move(_item)), is_closed(_is_closed) {}
bool operator==(const ReadResult& res) const {
return item == res.item && is_closed == res.is_closed;
}
// The item read from the channel or std::nullopt if the channel is empty.
// If so, callers can use |read_fd| to be notified when a read operation
// would succeed.
std::optional<T> item;
// Indicates the channel is closed. Readers can continue to read from the
// channel and any buffered elements will be correctly returned. Moreover,
// any future reads will also have |is_closed| == true and |read_fd| will be
// ready forever.
//
// Once a ReadResult is returned with |item| == std::nullopt and
// |is_closed| == true, no further values will ever be returned.
bool is_closed;
};
struct WriteResult {
WriteResult(bool _success, bool _is_closed)
: success(std::move(_success)), is_closed(_is_closed) {}
bool operator==(const WriteResult& res) const {
return success == res.success && is_closed == res.is_closed;
}
// Returns whether the write to the channel was successful. If this is
// false, callers can use |write_fd| to be notified when future writes
// would succeed. Note that callers should also check |is_closed| as another
// writer may have closed the channel.
bool success;
// Indicates that the channel is closed. If this value is true, |success|
// will be |false| Moreover, any further writes will continue to return
// |success| == false, |is_closed| == true and |write_fd| will be ready
// forever.
bool is_closed;
};
// Creates a channel with a capacity at least as large as |capacity_hint|. The
// capacity *must* be greater than zero.
//
// Note that it's possible that a capacity > |capacity_hint| will be chosen:
// it is implementation defined when this might happen.
explicit Channel(uint32_t capacity_hint) : elements_(capacity_hint) {
PERFETTO_DCHECK(capacity_hint > 0);
// It's very important that we make sure |write_fd| is ready to avoid
// deadlocks.
write_fd_.Notify();
}
// Attempts to read from the channel and returns the result of the attempt.
// See |ReadResult| for more information on the result.
PERFETTO_WARN_UNUSED_RESULT ReadResult ReadNonBlocking() {
std::lock_guard<std::mutex> lock(mutex_);
if (elements_.empty()) {
return ReadResult(std::nullopt, is_closed_);
}
if (elements_.capacity() == elements_.size()) {
write_fd_.Notify();
}
T value = std::move(elements_.front());
elements_.pop_front();
if (!is_closed_ && elements_.empty()) {
read_fd_.Clear();
}
return ReadResult(std::move(value), is_closed_);
}
// Attempts to write to the channel and returns the result of the attempt.
// See |WriteResult| for more information on the result.
//
// IMPORTANT: if this function returns |success| == false, |element| *will
// not* be modified. This allows the caller to try again with the same value.
PERFETTO_WARN_UNUSED_RESULT WriteResult WriteNonBlocking(T&& element) {
std::lock_guard<std::mutex> lock(mutex_);
if (is_closed_) {
return WriteResult{false, true};
}
if (elements_.size() == elements_.capacity()) {
return WriteResult{false, false};
}
if (elements_.empty()) {
read_fd_.Notify();
}
elements_.emplace_back(std::move(element));
if (elements_.size() == elements_.capacity()) {
write_fd_.Clear();
}
return WriteResult{true, false};
}
// Closes the channel for to any further writes.
//
// Note: this function will make both |read_fd| and |write_fd| ready to
// avoid deadlocks. Callers should correctly handle |is_closed| being
// false from |ReadNonBlocking| and |WriteNonBlocking| to stop watching the
// fds to avoid poll returning immediately.
//
// We prefer this behaviour as it's a lot more obvious something is wrong when
// it spins and takes 100% CPU rather than silently deadlocking.
void Close() {
std::lock_guard<std::mutex> lock(mutex_);
is_closed_ = true;
// Make both fds ready to avoid deadlocks.
read_fd_.Notify();
write_fd_.Notify();
}
// Notification FD for when |ReadNonBlocking| would succeed. Can be useful to
// pass to AddFileDescriptorWatch to read data from the channel.
base::PlatformHandle read_fd() const { return read_fd_.fd(); }
// Notification FD for when |WriteNonBlocking| would succeed. Can be useful to
// pass to AddFileDescriptorWatch to send data through the channel.
base::PlatformHandle write_fd() const { return write_fd_.fd(); }
private:
std::mutex mutex_;
base::CircularQueue<T> elements_;
bool is_closed_ = false;
base::EventFd read_fd_;
base::EventFd write_fd_;
};
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_THREADING_CHANNEL_H_

View file

@ -0,0 +1,149 @@
/*
* Copyright (C) 2023 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 INCLUDE_PERFETTO_EXT_BASE_THREADING_FUTURE_H_
#define INCLUDE_PERFETTO_EXT_BASE_THREADING_FUTURE_H_
#include <memory>
#include <type_traits>
#include "perfetto/ext/base/status_or.h"
#include "perfetto/ext/base/threading/future_combinators.h"
#include "perfetto/ext/base/threading/poll.h"
namespace perfetto {
namespace base {
// Creates a Future<T> from P, a subclass of FuturePollable<T>.
//
// T generally is a primitive (e.g. int, string, double) or structs of
// primitives but any can also be any moveable type.
//
// This function follows the same pattern of std::make_unique, std::make_shared
// etc.
template <typename P, typename... Args, typename T = typename P::PollT>
Future<T> MakeFuture(Args... args) {
return Future<T>(
std::unique_ptr<FuturePollable<T>>(new P(std::forward<Args>(args)...)));
}
// A value of type T which is computed asynchronously.
//
// The result of long running compute/IO operations may not be available
// immediately. This class acts as a representation of the value which will be
// produced at some point in the future. Callers can then be notified of the
// result once it's available to be processed.
//
// This class takes heavy inspiration from the implementation of Futures in
// Rust. Specifically, this implementation is:
// - pull-based/lazy: Futures do nothing until "polled" i.e. driven to
// completion by a base::TaskRunner. The implementation of this is provided
// by base::TaskRunnerPoller.
// - backpressured: because futures are "polled", the result is only
// requested when it can be processed on the base::TaskRunner thread.
// - cancellable: by just destroying the future the computation can be
// cancelled. Note, that the implementation of the source future still needs
// to propogate cancellation across thread/socket/pipe boundary.
//
// Note: Futures *must* be polled on the same thread on which they were created.
// The |SpawnResultFuture| can be used to move the results of Futures between
// threads in a safe manner.
//
// Implementation note:
// An important point to note is that Future<T> is a final class. Implementation
// of Future<T>::Poll happens through an indirection layer by implementing the
// FuturePollable<T> interface. This allows for the
// unique_ptr<FuturePollable<T>> to be hidden, making callsites nicer while
// also allowing useful "helper" functions like |ContinueWith| to live on the
// class rather than as free functions.
template <typename T>
class Future final {
public:
using PollT = T;
// Creates a Future from a |FuturePollable<T>|. Prefer using |MakeFuture|
// instead of this function.
explicit Future(std::unique_ptr<FuturePollable<T>> pollable)
: pollable_(std::move(pollable)) {}
// Intentionally implicit to allow for ergonomic definition of functions
// returning Future<T> for any T.
Future(T item) : pollable_(new ImmediateImpl<T>(std::move(item))) {}
// Intentionally implicit to allow for egonomic definition of functions
// returning Future<StatusOr<T>> by simply returning ErrStatus.
// The enable_if is necessary because this definition is the same as the above
// constructor in cases where T = base::Status.
template <typename U = T,
typename = std::enable_if_t<!std::is_same_v<Status, U>>>
Future(Status status) : Future(T(std::move(status))) {}
// Intentionally implicit to allow for egonomic definition of functions
// returning Future<StatusOr<T>> by simply returning T.
template <typename U = T, typename = typename U::value_type>
Future(typename U::value_type val) : Future(T(std::move(val))) {}
// Operator used to chain operations on Futures. The result T produced by
// |this| is passed to |fn| which itself returns a Future<U>. The return value
// of this function is a Future<U> which encapsulates both the operation done
// by |this| as well as by the Future<U> returned by |fn|.
//
// Usage:
// ```
// Future<int> MySpecialFutureFn();
// Future<std::string> IntToStringInBackground(int);
//
// MySpecialFutureFn().ContinueWith([](int x) -> Future<std::string> {
// return IntToStringInBackground(x);
// });
// ```
template <typename Function /* Future<U>(T) */,
typename U = FutureReturn<Function, T>>
Future<U> ContinueWith(Function fn) && {
return MakeFuture<ContinueWithImpl<Function, T>>(std::move(*this),
std::move(fn));
}
// Checks if the computation backing this Future<T> has finished.
//
// Returns a FuturePollResult<T> which is a essentially a
// variant<PendingPollResult, T>. If PendingPollResult is returned, |ctx| will
// be used to register interest in the various fds which are "blocking" this
// future from finishing. If T is returned, Poll *must not* be called again.
FuturePollResult<T> Poll(PollContext* ctx) { return pollable_->Poll(ctx); }
private:
// TOOD(lalitm): if performance becomes a problem, this can be changed to
// something more efficient e.g. either storage in a stack allocated buffer
// or with bump-pointer allocation. In the current usage this is not a
// performance bottleneck and so this is not important enough to invest time
// into fixing.
std::unique_ptr<FuturePollable<T>> pollable_;
};
// Alias to shorten type defintions for Future<Status> which is common in
// the codebase.
using StatusFuture = Future<Status>;
// Alias to shorten type defintions for Future<StatusOr<T>> which is common
// in the codebase.
template <typename T>
using StatusOrFuture = Future<StatusOr<T>>;
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_THREADING_FUTURE_H_

View file

@ -0,0 +1,76 @@
/*
* Copyright (C) 2023 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 INCLUDE_PERFETTO_EXT_BASE_THREADING_FUTURE_COMBINATORS_H_
#define INCLUDE_PERFETTO_EXT_BASE_THREADING_FUTURE_COMBINATORS_H_
#include <optional>
#include <utility>
#include <vector>
#include "perfetto/base/status.h"
#include "perfetto/ext/base/threading/poll.h"
namespace perfetto {
namespace base {
template <typename T>
class Future;
// For a Function which Future<U>, returns the U.
template <typename Function, typename T>
using FutureReturn = typename std::invoke_result<Function, T>::type::PollT;
// Implementation of FuturePollable for creating a Future<T> from a T.
template <typename T>
class ImmediateImpl : public FuturePollable<T> {
public:
explicit ImmediateImpl(T value) : value_(std::move(value)) {}
FuturePollResult<T> Poll(PollContext*) override { return std::move(value_); }
private:
T value_;
};
// Implementation of FuturePollable backing Future::ContinueWith.
template <typename Function, typename A, typename B = FutureReturn<Function, A>>
class ContinueWithImpl : public FuturePollable<B> {
public:
ContinueWithImpl(Future<A> first, Function second_fn)
: first_(std::move(first)), second_fn_(std::move(second_fn)) {}
FuturePollResult<B> Poll(PollContext* context) override {
PERFETTO_CHECK((first_ && second_fn_) || second_);
if (first_) {
ASSIGN_OR_RETURN_IF_PENDING_FUTURE(res, first_->Poll(context));
first_ = std::nullopt;
second_ = (*second_fn_)(std::move(res));
second_fn_ = std::nullopt;
}
return second_->Poll(context);
}
private:
std::optional<Future<A>> first_;
std::optional<Function> second_fn_;
std::optional<Future<B>> second_;
};
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_THREADING_FUTURE_COMBINATORS_H_

View file

@ -0,0 +1,245 @@
/*
* Copyright (C) 2023 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 INCLUDE_PERFETTO_EXT_BASE_THREADING_POLL_H_
#define INCLUDE_PERFETTO_EXT_BASE_THREADING_POLL_H_
#include <optional>
#include <variant>
#include "perfetto/base/flat_set.h"
#include "perfetto/base/platform_handle.h"
namespace perfetto {
namespace base {
// Forward declarations.
class PollContext;
// "Void" type for futures: this type can be used when a Future/Stream wants
// to return no value. We cannot use void directly because it causes all sorts
// of subtle issues with templates.
struct FVoid {};
// Indicates that the Future is not ready to produce data at the moment but
// will do so at a later date.
struct PendingPollResult {};
// Return value of Future<T>::Poll.
//
// Essentially a wrapper around std::variant<T, PendingPollResult> but with
// higher level API.
template <typename T>
class FuturePollResult {
public:
using PollT = T;
// Intentionally implicit to allow idiomatic returns.
FuturePollResult(const PendingPollResult&) : inner_(PendingPollResult()) {}
FuturePollResult(T item) noexcept : inner_(std::move(item)) {}
// Returns whether the Future is still pending.
bool IsPending() const {
return std::holds_alternative<PendingPollResult>(inner_);
}
// The real value inside this result: requires !IsPending().
T& item() {
PERFETTO_DCHECK(!IsPending());
return std::get<T>(inner_);
}
const T& item() const {
PERFETTO_DCHECK(!IsPending());
return std::get<T>(inner_);
}
// The real value inside this result: requires !IsPending().
T* operator->() { return &item(); }
const T* operator->() const { return &item(); }
private:
std::variant<PendingPollResult, T> inner_;
};
// Interface for implementing the Future<T>::Poll function.
//
// This is essentially a variant of the common PIMPL (pointer to impl) pattern
// used in C++ to allow having different implementations for Future<T>::Poll.
//
// We are using this instead of having an abstract function in Future to avoid
// having to wrap Future in unique_ptr everywhere it's used.
//
// We could have used std::function<Result(PollContext*)> but not all
// implementations of FuturePollable are copyable. If we had C++23, we could use
// std::move_only_function but we are some years from being able to do that.
template <typename T>
class FuturePollable {
public:
using PollT = T;
virtual ~FuturePollable() = default;
// Implementation of the Poll function of a Future: see Future documentation
// for how this should be implemented.
virtual FuturePollResult<T> Poll(PollContext*) = 0;
};
// Indicates that the Stream has been exhausted and no more values will be
// returned.
struct DonePollResult {};
// Return value of Stream<T>::Poll.
//
// Essentially a wrapper around std::variant<T, PendingPollResult,
// DonePollResult> but with higher level API.
template <typename T>
class StreamPollResult {
public:
using PollT = T;
// Intentionally implicit to allow idiomatic returns.
StreamPollResult(const PendingPollResult&) : inner_(PendingPollResult()) {}
StreamPollResult(const DonePollResult&) : inner_(DonePollResult()) {}
StreamPollResult(T item) : inner_(std::move(item)) {}
// Returns whether the Stream is still pending.
bool IsPending() const {
return std::holds_alternative<PendingPollResult>(inner_);
}
// Returns whether the Stream is done.
bool IsDone() const { return std::holds_alternative<DonePollResult>(inner_); }
// The real value inside this result: requires !IsPending() and !IsDone().
T& item() {
PERFETTO_DCHECK(!IsPending());
PERFETTO_DCHECK(!IsDone());
return std::get<T>(inner_);
}
const T& item() const {
PERFETTO_DCHECK(!IsPending());
PERFETTO_DCHECK(!IsDone());
return std::get<T>(inner_);
}
// The real value inside this result: requires !IsPending() and !IsDone().
T* operator->() { return &item(); }
const T* operator->() const { return &item(); }
private:
std::variant<PendingPollResult, DonePollResult, T> inner_;
};
// Interface for implementing the Stream<T>::Poll function.
//
// This is essentially analagous to FuturePollable<T> for streams: check the
// documentation of that class for why this exists.
template <typename T>
class StreamPollable {
public:
using PollT = T;
virtual ~StreamPollable() = default;
// Implementation of the Poll function of a Stream: see Stream documentation
// for how this should be implemented.
virtual StreamPollResult<T> PollNext(PollContext*) = 0;
};
// Context class passed to Pollable classes.
//
// Implementations of Pollable which simply wrap another Pollable will use
// this as an opaque parameter to pass on.
//
// "Source" pollables (i.e. Pollables dealing directly with FDs) should call
// |RegisterInterested| when the FD returns EAGAIN/EWOULDBLOCK with the
// PollContext passed in.
class PollContext {
public:
explicit PollContext(FlatSet<PlatformHandle>* interested_handles,
const FlatSet<PlatformHandle>* ready_handles)
: interested_handles_(interested_handles),
ready_handles_(ready_handles) {}
PollContext(PollContext&&) = default;
PollContext& operator=(PollContext&&) = default;
// Called by implementations of Future<T> to indicate that Poll should be
// called again when |handle(s)| are ready for reading (or have been closed).
void RegisterInterested(PlatformHandle handle) {
interested_handles_->insert(handle);
}
void RegisterAllInterested(const FlatSet<PlatformHandle>& handles) {
for (PlatformHandle handle : handles) {
RegisterInterested(handle);
}
}
// Returns a set of all the fds which were marked as "ready" by the operating
// system (i.e. POLLIN/POLLHUP on Linux).
const FlatSet<PlatformHandle>& ready_handles() const {
return *ready_handles_;
}
private:
PollContext(const PollContext&) = delete;
PollContext& operator=(const PollContext&) = delete;
FlatSet<PlatformHandle>* interested_handles_ = nullptr;
const FlatSet<PlatformHandle>* ready_handles_ = nullptr;
};
// Evaluates |expr|, which should return a FuturePollResult. If IsPending is
// true, returns base::PendingPollResult().
//
// Example usage:
//
// Future<int> MyIntReturningFutureFn();
//
// FuturePollResult<std::string> Poll(PollContext* ctx) {
// // res will be of type "int"
// ASSIGN_OR_RETURN_IF_PENDING_FUTURE(res, MyIntReturningFutureFn());
// return std::to_string(*foo);
// }
#define ASSIGN_OR_RETURN_IF_PENDING_FUTURE(var, expr) \
auto assign_and_return_if_poll_##var = (expr); \
if (assign_and_return_if_poll_##var.IsPending()) \
return base::PendingPollResult(); \
auto var = std::move(assign_and_return_if_poll_##var.item())
// Evaluates |expr|, which should return a PollResult. If IsPending is
// true, returns base::PendingPollResult().
//
// Example usage:
//
// Strean<int> MyIntReturningStreamFn();
//
// StreamPollResult<std::string> Poll(PollContext* ctx) {
// ASSIGN_OR_RETURN_IF_PENDING_STREAM(res, MyIntReturningStreamFn());
// if (res.IsDone()) {
// return DonePollResult();
// }
// return std::to_string(*foo);
// }
#define ASSIGN_OR_RETURN_IF_PENDING_STREAM(var, expr) \
auto var = (expr); \
if (var.IsPending()) \
return base::PendingPollResult()
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_THREADING_POLL_H_

View file

@ -0,0 +1,149 @@
/*
* Copyright (C) 2023 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 INCLUDE_PERFETTO_EXT_BASE_THREADING_SPAWN_H_
#define INCLUDE_PERFETTO_EXT_BASE_THREADING_SPAWN_H_
#include <atomic>
#include <cstdint>
#include <functional>
#include <memory>
#include <mutex>
#include <optional>
#include <utility>
#include <vector>
#include "perfetto/base/compiler.h"
#include "perfetto/base/flat_set.h"
#include "perfetto/base/platform_handle.h"
#include "perfetto/base/task_runner.h"
#include "perfetto/ext/base/event_fd.h"
#include "perfetto/ext/base/flat_hash_map.h"
#include "perfetto/ext/base/thread_checker.h"
#include "perfetto/ext/base/threading/channel.h"
#include "perfetto/ext/base/threading/future.h"
#include "perfetto/ext/base/threading/poll.h"
#include "perfetto/ext/base/threading/stream.h"
#include "perfetto/ext/base/threading/stream_combinators.h"
#include "perfetto/ext/base/threading/util.h"
#include "perfetto/ext/base/uuid.h"
#include "perfetto/ext/base/weak_ptr.h"
namespace perfetto {
namespace base {
class PolledFuture;
// A RAII object which tracks the polling of a Future.
//
// When this object is dropped, the backing Future will be cancelled as
// soon as possible. In practice, the cancellation happens on the TaskRunner
// thread so there can be some delay.
class SpawnHandle {
public:
SpawnHandle(TaskRunner* task_runner, std::function<Future<FVoid>()> fn);
SpawnHandle(SpawnHandle&&) = default;
SpawnHandle& operator=(SpawnHandle&&) = default;
~SpawnHandle();
private:
SpawnHandle(const SpawnHandle&) = delete;
SpawnHandle& operator=(const SpawnHandle&) = delete;
TaskRunner* task_runner_ = nullptr;
std::shared_ptr<std::unique_ptr<PolledFuture>> polled_future_;
};
// "Spawns" a Future<FVoid> on the given TaskRunner and returns an RAII
// SpawnHandle which can be used to cancel the spawn.
//
// Spawning a Future means to poll it to completion. In Perfetto, this is done
// by using a TaskRunner object to track FD readiness and polling the Future
// when progress can be made.
//
// The returned SpawnHandle should be stashed as it is responsible for the
// lifetime of the pollling. If the SpawnHandle is dropped, the Future is
// cancelled and dropped ASAP (this happens on the TaskRunner thread so there
// can be some delay).
PERFETTO_WARN_UNUSED_RESULT inline SpawnHandle SpawnFuture(
TaskRunner* task_runner,
std::function<Future<FVoid>()> fn) {
return SpawnHandle(task_runner, std::move(fn));
}
// Variant of |SpawnFuture| for a Stream<T> allowing returning items of T.
//
// The Stream<T> returned by this function can be consumed on any thread, not
// just the thread which ran this function.
//
// Dropping the returned stream does not affect the polling of the underlying
// stream (i.e. the stream returned by |fn|); the polled values will simply be
// dropped.
//
// Dropping the returned SpawnHandle causes the underlying stream to be
// cancelled and dropped ASAP (this happens on the TaskRunner thread so there
// can be some delay). The returned channel will return all the values that were
// produced by the underlying stream before the cancellation.
template <typename T>
PERFETTO_WARN_UNUSED_RESULT std::pair<SpawnHandle, Stream<T>> SpawnResultStream(
TaskRunner* runner,
std::function<Stream<T>()> fn) {
class AllVoidCollector : public Collector<FVoid, FVoid> {
public:
std::optional<FVoid> OnNext(FVoid) override { return std::nullopt; }
FVoid OnDone() override { return FVoid(); }
};
auto channel = std::make_shared<Channel<T>>(4);
auto control = std::make_shared<Channel<FVoid>>(1);
SpawnHandle handle(runner, [channel, control, fn = std::move(fn)]() {
return fn()
.MapFuture([channel, control](T value) mutable {
if (control->ReadNonBlocking().is_closed) {
return base::Future<base::FVoid>(base::FVoid());
}
return WriteChannelFuture(channel.get(), std::move(value));
})
.Concat(OnDestroyStream<FVoid>([c = channel]() { c->Close(); }))
.template Collect<base::FVoid>(std::make_unique<AllVoidCollector>());
});
Stream<T> stream = ReadChannelStream(channel.get())
.Concat(OnDestroyStream<T>([channel, control]() {
// Close the control stream and drain an element from
// the channel to unblock it in case it was blocked.
// NOTE: the ordering here is important as we could
// deadlock if it was the other way around!
control->Close();
base::ignore_result(channel->ReadNonBlocking());
}));
return std::make_pair(std::move(handle), std::move(stream));
}
// Variant of |SpawnResultStream| but for Future<T>.
template <typename T>
PERFETTO_WARN_UNUSED_RESULT inline std::pair<SpawnHandle, Future<T>>
SpawnResultFuture(TaskRunner* task_runner, std::function<Future<T>()> fn) {
auto [handle, stream] = SpawnResultStream<T>(
task_runner, [fn = std::move(fn)]() { return StreamFromFuture(fn()); });
return std::make_pair(std::move(handle), std::move(stream).Collect(
ToFutureCheckedCollector<T>()));
}
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_THREADING_SPAWN_H_

View file

@ -0,0 +1,190 @@
/*
* Copyright (C) 2023 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 INCLUDE_PERFETTO_EXT_BASE_THREADING_STREAM_H_
#define INCLUDE_PERFETTO_EXT_BASE_THREADING_STREAM_H_
#include <functional>
#include <memory>
#include <vector>
#include "perfetto/base/status.h"
#include "perfetto/ext/base/status_or.h"
#include "perfetto/ext/base/threading/future.h"
#include "perfetto/ext/base/threading/stream_combinators.h"
namespace perfetto {
namespace base {
// Creates a Stream<T> from P, a subclass of StreamPollable<T>.
//
// This function follows the same pattern of std::make_unique, std::make_shared
// etc.
template <typename P, typename... Args>
Stream<typename P::PollT> MakeStream(Args... args) {
return Stream<typename P::PollT>(
std::unique_ptr<StreamPollable<typename P::PollT>>(
new P(std::forward<Args>(args)...)));
}
// An asynchronous iterator for values of type T.
//
// If Future<T> is an asynchronous version of T, Stream<T> is an asynchronous
// version of Iterator<T>. Long-running compute/IO operations which return
// multiple values can be represented with a Stream<T>.
//
// Note: Streams *must* be polled on the same thread on which they were
// created. The |SpawnResultStreams| can be used to move of the results of
// Streams between threads in a safe manner.
//
// Refer to the class documentation for Future<T> as most of the features and
// implementation of Future<T> also apply to Stream<T>.
template <typename T>
class Stream {
public:
using PollableItem = T;
// Creates a Stream from a |StreamPollable<T>|. Prefer using |MakeStream|
// instead of this function.
explicit Stream(std::unique_ptr<StreamPollable<T>> pollable)
: pollable_(std::move(pollable)) {}
// Converts a Stream<T> to Stream<U>. This works by applying |map_fn| to each
// element in T and then polling the returned Future<U> to completion.
template <typename Function /* = Future<U>(T) */>
Stream<FutureReturn<Function, T>> MapFuture(Function map_fn) && {
return MakeStream<MapFutureStreamImpl<Function, T>>(std::move(*this),
std::move(map_fn));
}
// Creates a stream which fully polls |this| and then polls |concat| to
// completion.
Stream<T> Concat(Stream<T> concat) && {
return MakeStream<ConcatStreamImpl<T>>(std::move(*this), std::move(concat));
}
// Converts a Stream<T> to Future<U> by collecting elements using |collector|.
// See documentation on |Collector| for how to implement one.
template <typename U>
Future<U> Collect(std::unique_ptr<Collector<T, U>> collector) && {
return MakeFuture<CollectImpl<T, U>>(std::move(*this),
std::move(collector));
}
// Checks if the computation backing this Stream<T> has finished.
//
// Returns a StreamPollResult<T> which is a essentially a
// variant<PendingPollResult, DonePollResult T>. If PendingPollResult is
// returned, |ctx| will be used to register interest in the various fds which
// are "blocking" this future from finishing. If DonePollResult is returned,
// Poll *must not* be called again.
StreamPollResult<T> PollNext(PollContext* ctx) {
return pollable_->PollNext(ctx);
}
private:
std::unique_ptr<StreamPollable<T>> pollable_;
};
// Alias to shorten type defintions for Stream<Status> which is common in
// the codebase.
using StatusStream = Stream<Status>;
// Alias to shorten type defintions for Stream<StatusOr<T>> which is common
// in the codebase.
template <typename T>
using StatusOrStream = Stream<StatusOr<T>>;
// Creates a Stream<T> which returns the next value inside |vector| every time
// Stream<T>::Poll is called.
template <typename T>
Stream<T> StreamFrom(std::vector<T> vector) {
return MakeStream<ImmediateStreamImpl<T>>(std::move(vector));
}
// Creates a Stream<T> which immediately returns DonePollResult when polled.
template <typename T>
Stream<T> EmptyStream() {
return StreamFrom(std::vector<T>());
}
// Creates a Stream<T> which returns |first| and each of |rest| in sequence when
// polled.
template <typename T, typename... Ts>
Stream<T> StreamOf(T first, Ts... rest) {
std::vector<T> values;
AddAllToVector(values, std::forward<T>(first), std::forward<Ts>(rest)...);
return StreamFrom(std::move(values));
}
// Creates a Stream<T> which returns the value of |future| before completing.
template <typename T>
Stream<T> StreamFromFuture(Future<T> future) {
return StreamOf(std::move(future)).MapFuture([](Future<T> value) {
return value;
});
}
// Creates a stream which returns no elements but calls |fn| in the destructor
// of the returned stream.
//
// This function can be used to do resource management for a stream by making
// the passed |fn| own the resources used by any "upstream" sources and then
// Concat-ing this stream with the upstream.
template <typename T, typename Function>
Stream<T> OnDestroyStream(Function fn) {
return MakeStream<OnDestroyStreamImpl<T, Function>>(std::move(fn));
}
// Creates a Stream<T> returning values generated by each stream in |streams| as
// soon as they are produced without preserving ordering.
//
// The returned Stream<T> keeps the amount of Poll calls to the inner |streams|,
// to a minimum only calling Poll for the Streams which are marked are ready
// in the PollContext.
template <typename T>
Stream<T> FlattenStreams(std::vector<Stream<T>> streams) {
return MakeStream<FlattenImpl<T>>(std::move(streams));
}
// Collector for Stream<Status>::Collect() which immediately resolves the
// returned Future when an error status is detected. Resolves with
// OkStatus once the entire stream finishes after returning all OkStatus().
inline std::unique_ptr<Collector<Status, Status>> AllOkCollector() {
return std::make_unique<AllOkCollectorImpl>();
}
// Collector for Stream<T>::Collect() which ensures the stream returns *exactly*
// one T before completing. Crashes if either a) no values are produced by
// the Stream, b) more than one value is produced by the Stream.
template <typename T>
inline std::unique_ptr<Collector<T, T>> ToFutureCheckedCollector() {
return std::make_unique<FutureCheckedCollectorImpl<T>>();
}
// Collector for Stream<StatusOr<T>>::Collect() which returns a vector
// containing all the successful results from the stream. If any element is an
// error, short-circuits the stream with the error.
template <typename T>
inline std::unique_ptr<Collector<StatusOr<T>, StatusOr<std::vector<T>>>>
StatusOrVectorCollector() {
return std::make_unique<StatusOrVectorCollectorImpl<T>>();
}
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_THREADING_STREAM_H_

View file

@ -0,0 +1,315 @@
/*
* Copyright (C) 2023 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 INCLUDE_PERFETTO_EXT_BASE_THREADING_STREAM_COMBINATORS_H_
#define INCLUDE_PERFETTO_EXT_BASE_THREADING_STREAM_COMBINATORS_H_
#include <memory>
#include <optional>
#include <utility>
#include <vector>
#include "perfetto/base/status.h"
#include "perfetto/ext/base/status_or.h"
#include "perfetto/ext/base/threading/future_combinators.h"
#include "perfetto/ext/base/threading/poll.h"
namespace perfetto {
namespace base {
template <typename T>
class Stream;
// Helper function for adding all the elements in parameter pack to a vector.
template <typename T, typename... Elements>
void AddAllToVector(std::vector<T>&) {}
template <typename T, typename... Elements>
void AddAllToVector(std::vector<T>& vec, T first, Elements... rest) {
vec.emplace_back(std::forward<T>(first));
AddAllToVector(vec, std::forward<Elements>(rest)...);
}
// For a Function which returns Stream<U>, returns the U.
template <typename Function, typename T>
using StreamReturn =
typename std::invoke_result<Function, T>::type::PollableItem;
// Implementation of StreamPollable for creating a Stream<T> from a
// std::vector<T>.
template <typename T>
class ImmediateStreamImpl : public StreamPollable<T> {
public:
explicit ImmediateStreamImpl(std::vector<T> values)
: values_(std::move(values)) {}
StreamPollResult<T> PollNext(PollContext*) override {
if (index_ >= values_.size()) {
return DonePollResult();
}
return StreamPollResult<T>(std::move(values_[index_++]));
}
private:
std::vector<T> values_;
uint32_t index_ = 0;
};
// Implementation of a StreamPollable for creating a Stream<U> from a Stream<T>
// and a functor with prototype Future<U>(T).
template <typename Function, typename T>
class MapFutureStreamImpl : public StreamPollable<FutureReturn<Function, T>> {
public:
using U = FutureReturn<Function, T>;
MapFutureStreamImpl(Stream<T> stream, Function map_fn)
: stream_(std::move(stream)), map_fn_(std::move(map_fn)) {}
StreamPollResult<U> PollNext(PollContext* context) override {
if (!future_) {
ASSIGN_OR_RETURN_IF_PENDING_STREAM(res, stream_.PollNext(context));
if (res.IsDone()) {
return DonePollResult();
}
future_ = map_fn_(std::move(res.item()));
}
ASSIGN_OR_RETURN_IF_PENDING_FUTURE(res, future_->Poll(context));
future_ = std::nullopt;
return res;
}
private:
Stream<T> stream_;
Function map_fn_;
std::optional<Future<U>> future_;
};
// Implementation of a StreamPollable for creating a concatenating two streams
// together.
template <typename T>
class ConcatStreamImpl : public StreamPollable<T> {
public:
explicit ConcatStreamImpl(Stream<T> first, Stream<T> second)
: first_(std::move(first)), second_(std::move(second)) {}
StreamPollResult<T> PollNext(PollContext* context) override {
if (first_) {
ASSIGN_OR_RETURN_IF_PENDING_STREAM(res, first_->PollNext(context));
if (!res.IsDone()) {
return res.item();
}
first_ = std::nullopt;
}
return second_.PollNext(context);
}
private:
std::optional<Stream<T>> first_;
Stream<T> second_;
};
// Implementation of a StreamPollable for creating a Stream<T> from a
// std::vector<Stream<T>>. Values are returned from the inner streams as soon as
// they are available.
template <typename T>
class FlattenImpl : public StreamPollable<T> {
public:
explicit FlattenImpl(std::vector<Stream<T>> streams)
: registered_handles_(static_cast<uint32_t>(streams.size())) {
for (auto& stream : streams) {
streams_.emplace_back(std::move(stream));
}
}
StreamPollResult<T> PollNext(PollContext* upstream) override {
for (uint32_t i = 0; i < streams_.size(); ++i) {
auto& stream = streams_[i];
if (!stream) {
continue;
}
std::optional<PollContext> ctx = PollContextForStream(upstream, i);
if (!ctx) {
continue;
}
StreamPollResult<T> res = stream->PollNext(&*ctx);
if (res.IsPending()) {
PERFETTO_CHECK(!registered_handles_[i].empty());
continue;
}
if (!res.IsDone()) {
return res;
}
// StreamPollable has returned EOF. Clear it and the registered handles
// out.
stream = std::nullopt;
++eof_streams_;
registered_handles_[i].clear();
}
// Every child stream being EOF means we have reached EOF as well.
if (eof_streams_ == streams_.size()) {
return DonePollResult();
}
// Every remaining stream must be pending so we can make no further
// progress. Register all the child handles with the context and return.
for (const FlatSet<PlatformHandle>& handles : registered_handles_) {
upstream->RegisterAllInterested(handles);
}
return PendingPollResult();
}
private:
std::optional<PollContext> PollContextForStream(PollContext* upstream,
uint32_t stream_idx) {
FlatSet<PlatformHandle>& state = registered_handles_[stream_idx];
if (state.empty()) {
return PollContext(&state, &upstream->ready_handles());
}
for (PlatformHandle handle : upstream->ready_handles()) {
if (state.count(handle)) {
state.clear();
return PollContext(&state, &upstream->ready_handles());
}
}
return std::nullopt;
}
std::vector<std::optional<Stream<T>>> streams_;
std::vector<FlatSet<PlatformHandle>> registered_handles_;
uint32_t eof_streams_ = 0;
};
// Implementation of a Stream<T> which immediately completes and calls a
// function in the destructor.
template <typename T, typename Function>
class OnDestroyStreamImpl : public StreamPollable<T> {
public:
explicit OnDestroyStreamImpl(Function fn) : fn_(std::move(fn)) {}
~OnDestroyStreamImpl() override { fn_(); }
StreamPollResult<T> PollNext(PollContext*) override {
return DonePollResult();
}
private:
Function fn_;
};
// Interface for converting a Stream<T> into a Future<U>.
//
// The goal of this interface is to allow a Stream to be converted to a Future,
// allowing short-circuiting (i.e. allowing the Future to complete before
// the stream finishes).
//
// The flexibility of this interface allows both supporting the traditional
// notion of collecting i.e. converting a Stream<T> to a Future<vector<T>> but
// also more advanced functionality like completing a Future<Status> early
// when errors are detected, racing Future<T> against each other and returning
// the first value produced etc.
template <typename T, typename U>
class Collector {
public:
virtual ~Collector() = default;
// Receives the next item from a Stream<T>. If the wrapping Future<U> can be
// completed, returns the a value U which completes that future. Otherwise,
// returns std::nullopt.
virtual std::optional<U> OnNext(T value) = 0;
// Called when the stream has completed and returns the |U| which will be
// used to complete the future. This method will only be called if OnNext
// returned std::nullopt for every element in the stream.
virtual U OnDone() = 0;
};
// Implementation of a StreamPollable which converts a Stream<T> to a Future<U>
// using an implementation of Collector<T, U>.
template <typename T, typename U>
class CollectImpl : public FuturePollable<U> {
public:
explicit CollectImpl(Stream<T> stream,
std::unique_ptr<Collector<T, U>> collector)
: stream_(std::move(stream)), collector_(std::move(collector)) {}
FuturePollResult<U> Poll(PollContext* context) override {
for (;;) {
ASSIGN_OR_RETURN_IF_PENDING_STREAM(res, stream_.PollNext(context));
if (res.IsDone()) {
return collector_->OnDone();
}
std::optional<U> collected = collector_->OnNext(std::move(res.item()));
if (collected.has_value()) {
return std::move(collected.value());
}
}
}
private:
Stream<T> stream_;
std::unique_ptr<Collector<T, U>> collector_;
};
// Implementation for |AllOkCollector|.
class AllOkCollectorImpl : public Collector<Status, Status> {
public:
~AllOkCollectorImpl() override;
std::optional<Status> OnNext(Status status) override {
return status.ok() ? std::nullopt : std::make_optional(std::move(status));
}
Status OnDone() override { return OkStatus(); }
};
// Implementation for |ToFutureCheckedCollector|.
template <typename T>
class FutureCheckedCollectorImpl : public Collector<T, T> {
public:
std::optional<T> OnNext(T value) override {
PERFETTO_CHECK(!prev_value_);
prev_value_ = value;
return std::nullopt;
}
T OnDone() override { return *prev_value_; }
private:
std::optional<T> prev_value_;
};
// Implementation for |StatusOrVectorCollector|.
template <typename T>
class StatusOrVectorCollectorImpl
: public Collector<base::StatusOr<T>, base::StatusOr<std::vector<T>>> {
public:
std::optional<base::StatusOr<std::vector<T>>> OnNext(
base::StatusOr<T> val_or) override {
if (!val_or.ok()) {
return std::make_optional(val_or.status());
}
values_.emplace_back(std::move(val_or.value()));
return std::nullopt;
}
base::StatusOr<std::vector<T>> OnDone() override {
return std::move(values_);
}
private:
std::vector<T> values_;
};
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_THREADING_STREAM_COMBINATORS_H_

View file

@ -0,0 +1,80 @@
/*
* Copyright (C) 2023 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 INCLUDE_PERFETTO_EXT_BASE_THREADING_THREAD_POOL_H_
#define INCLUDE_PERFETTO_EXT_BASE_THREADING_THREAD_POOL_H_
#include <condition_variable>
#include <functional>
#include <list>
#include <mutex>
#include <optional>
#include <thread>
#include <vector>
#include "perfetto/base/task_runner.h"
namespace perfetto {
namespace base {
// Bounded thread pool designed for CPU-bound tasks.
//
// This is a classic bounded thread pool designed for running jobs which fully
// occupy the CPU without blocking. IO bound tasks which block for long periods
// of times will cause starvation for any other tasks which are waiting.
// IO-heavy tasks should use base::TaskRunner and async-IO instead of using this
// class.
//
// Threads are created when the thread pool is created and persist for the
// lifetime of the ThreadPool. No new threads are created after construction.
// When the ThreadPool is destroyed, any active tasks are completed and every
// thread joined before returning from the destructor.
//
// Tasks are executed in a FIFO order without any notion of priority. If a
// thread in the pool is free, it will be used to execute the task immediately.
// Otherwise, it will be queued for execution when any thread becomes available.
class ThreadPool {
public:
// Initializes this thread_pool |thread_count| threads.
explicit ThreadPool(uint32_t thread_count);
~ThreadPool();
// Submits a task for execution by any thread in this thread pool.
//
// This task should not block for IO as this can cause starvation.
void PostTask(std::function<void()>);
private:
void RunThreadLoop();
ThreadPool(ThreadPool&&) = delete;
ThreadPool& operator=(ThreadPool&&) = delete;
// Start of mutex protected members.
std::mutex mutex_;
std::list<std::function<void()>> pending_tasks_;
std::condition_variable thread_waiter_;
uint32_t thread_waiting_count_ = 0;
bool quit_ = false;
// End of mutex protected members.
std::vector<std::thread> threads_;
};
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_THREADING_THREAD_POOL_H_

View file

@ -0,0 +1,210 @@
/*
* Copyright (C) 2023 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 INCLUDE_PERFETTO_EXT_BASE_THREADING_UTIL_H_
#define INCLUDE_PERFETTO_EXT_BASE_THREADING_UTIL_H_
#include <functional>
#include <memory>
#include <optional>
#include <type_traits>
#include <utility>
#include <vector>
#include "perfetto/base/status.h"
#include "perfetto/base/task_runner.h"
#include "perfetto/ext/base/threading/channel.h"
#include "perfetto/ext/base/threading/future.h"
#include "perfetto/ext/base/threading/poll.h"
#include "perfetto/ext/base/threading/stream.h"
#include "perfetto/ext/base/threading/thread_pool.h"
#include "perfetto/ext/base/unix_task_runner.h"
namespace perfetto {
namespace base {
// Blocks the calling thread until |fd| is considered "readable". In Linux,
// this corresponds to |POLLOUT| or |POLLHUP| being returned if |fd| is polled.
// If |timeout_ms| is specified, waits that many ms before stopping.
//
// Returns true if the function returned because the fd was readable and false
// otherwise.
inline bool BlockUntilReadableFd(
base::PlatformHandle fd,
std::optional<uint32_t> timeout_ms = std::nullopt) {
bool is_readable = false;
base::UnixTaskRunner runner;
runner.AddFileDescriptorWatch(fd, [&runner, &is_readable]() {
is_readable = true;
runner.Quit();
});
if (timeout_ms) {
runner.PostDelayedTask([&runner]() { runner.Quit(); }, *timeout_ms);
}
runner.Run();
return is_readable;
}
// Creates a Stream<T> which returns all the data from |channel| and completes
// when |channel| is closed.
//
// Note: the caller retains ownership of the passed channel and must ensure that
// the channel outlives the lifetime of the returned stream.
template <typename T>
Stream<T> ReadChannelStream(Channel<T>* channel) {
class ReadImpl : public StreamPollable<T> {
public:
explicit ReadImpl(Channel<T>* reader) : reader_(reader) {}
StreamPollResult<T> PollNext(PollContext* ctx) override {
auto result = reader_->ReadNonBlocking();
if (!result.item.has_value()) {
if (result.is_closed) {
return DonePollResult();
}
ctx->RegisterInterested(reader_->read_fd());
return PendingPollResult();
}
return std::move(*result.item);
}
private:
Channel<T>* reader_ = nullptr;
};
return MakeStream<ReadImpl>(channel);
}
// Creates a Future<FVoid> which handles writing |item| into |channel|. The
// Future is completed when the item is succesfully written.
//
// Note: the caller retains ownership of the passed channel and must ensure that
// the channel outlives the lifetime of the returned future.
template <typename T>
Future<FVoid> WriteChannelFuture(Channel<T>* channel, T item) {
class WriteImpl : public FuturePollable<FVoid> {
public:
WriteImpl(Channel<T>* writer, T to_write)
: writer_(writer), to_write_(std::move(to_write)) {}
FuturePollResult<FVoid> Poll(PollContext* ctx) override {
auto res = writer_->WriteNonBlocking(std::move(to_write_));
PERFETTO_CHECK(!res.is_closed);
if (!res.success) {
ctx->RegisterInterested(writer_->write_fd());
return PendingPollResult();
}
return FVoid();
}
private:
Channel<T>* writer_ = nullptr;
T to_write_;
};
return MakeFuture<WriteImpl>(channel, std::move(item));
}
// Creates a Stream<T> which yields the result of executing |fn| on |pool|
// repeatedly. The returned stream only completes when |fn| returns
// std::nullopt.
//
// Callers can optionally specify a |on_destroy| function which is executed when
// the returned stream is destroyed. This is useful for informing the work
// spawned on the thread pool that the result is no longer necessary.
//
// The intended usage of this function is to schedule CPU intensive work on a
// background thread pool and receive regular "updates" on the progress by:
// a) breaking the work into chunks
// b) returning some indication of progress/partial results through |T|.
template <typename T>
Stream<T> RunOnThreadPool(
ThreadPool* pool,
std::function<std::optional<T>()> fn,
std::function<void()> on_destroy = [] {}) {
class RunOnPoolImpl : public StreamPollable<T> {
public:
explicit RunOnPoolImpl(ThreadPool* pool,
std::function<std::optional<T>()> fn,
std::function<void()> on_destroy)
: pool_(pool),
fn_(std::make_shared<std::function<std::optional<T>()>>(
std::move(fn))),
on_destroy_(std::move(on_destroy)),
channel_(new Channel<T>(1)),
channel_stream_(ReadChannelStream(channel_.get())) {
RunFn();
}
~RunOnPoolImpl() override { on_destroy_(); }
StreamPollResult<T> PollNext(PollContext* ctx) override {
ASSIGN_OR_RETURN_IF_PENDING_STREAM(res, channel_stream_.PollNext(ctx));
if (res.IsDone()) {
return DonePollResult();
}
RunFn();
return res;
}
private:
void RunFn() {
pool_->PostTask([channel = channel_, fn = fn_]() mutable {
auto opt_value = (*fn)();
if (!opt_value) {
// Clear out the function to ensure that any captured state is freed
// before we inform the caller.
fn.reset();
channel->Close();
return;
}
auto write_res =
channel->WriteNonBlocking(std::move(opt_value.value()));
PERFETTO_CHECK(write_res.success);
PERFETTO_CHECK(!write_res.is_closed);
});
}
ThreadPool* pool_ = nullptr;
std::shared_ptr<std::function<std::optional<T>()>> fn_;
std::function<void()> on_destroy_;
std::shared_ptr<Channel<T>> channel_;
base::Stream<T> channel_stream_;
};
return MakeStream<RunOnPoolImpl>(pool, std::move(fn), std::move(on_destroy));
}
// Creates a Future<T> which yields the result of executing |fn| on |pool|. The
// returned completes with the return value of |fn|.
//
// The intended usage of this function is to schedule CPU intensive work on a
// background thread pool and have the result returned when available.
template <typename T>
Future<T> RunOnceOnThreadPool(ThreadPool* pool, std::function<T()> fn) {
return RunOnThreadPool<T>(
pool,
[done = false, fn = std::move(fn)]() mutable -> std::optional<T> {
if (done) {
return std::nullopt;
}
done = true;
return fn();
})
.Collect(base::ToFutureCheckedCollector<T>());
}
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_THREADING_UTIL_H_

View file

@ -0,0 +1,464 @@
/*
* Copyright (C) 2017 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 INCLUDE_PERFETTO_EXT_BASE_UNIX_SOCKET_H_
#define INCLUDE_PERFETTO_EXT_BASE_UNIX_SOCKET_H_
#include <stdint.h>
#include <sys/types.h>
#include <memory>
#include <string>
#include <utility>
#include "perfetto/base/build_config.h"
#include "perfetto/base/compiler.h"
#include "perfetto/base/export.h"
#include "perfetto/base/logging.h"
#include "perfetto/base/platform_handle.h"
#include "perfetto/ext/base/scoped_file.h"
#include "perfetto/ext/base/utils.h"
#include "perfetto/ext/base/weak_ptr.h"
struct msghdr;
namespace perfetto {
namespace base {
// Define the ScopedSocketHandle type.
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
int CloseSocket(SocketHandle); // A wrapper around ::closesocket().
using ScopedSocketHandle =
ScopedResource<SocketHandle, CloseSocket, static_cast<SocketHandle>(-1)>;
#else
using ScopedSocketHandle = ScopedFile;
#endif
class TaskRunner;
// Use arbitrarily high values to avoid that some code accidentally ends up
// assuming that these enum values match the sysroot's SOCK_xxx defines rather
// than using MkSockType() / MkSockFamily().
enum class SockType { kStream = 100, kDgram, kSeqPacket };
enum class SockFamily { kUnspec = 0, kUnix = 200, kInet, kInet6, kVsock };
// Controls the getsockopt(SO_PEERCRED) behavior, which allows to obtain the
// peer credentials.
enum class SockPeerCredMode {
// Obtain the peer credentials immediately after connection and cache them.
kReadOnConnect = 0,
// Don't read peer credentials at all. Calls to peer_uid()/peer_pid() will
// hit a DCHECK and return kInvalidUid/Pid in release builds.
kIgnore = 1,
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) || \
PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
kDefault = kIgnore,
#else
kDefault = kReadOnConnect,
#endif
};
// Returns the socket family from the full addres that perfetto uses.
// Addr can be:
// - /path/to/socket : for linked AF_UNIX sockets.
// - @abstract_name : for abstract AF_UNIX sockets.
// - 1.2.3.4:8080 : for Inet sockets.
// - [::1]:8080 : for Inet6 sockets.
// - vsock://-1:3000 : for VM sockets.
SockFamily GetSockFamily(const char* addr);
// Returns whether inter-process shared memory is supported for the socket.
inline bool SockShmemSupported(SockFamily sock_family) {
#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
return sock_family == SockFamily::kUnix;
#else
base::ignore_result(sock_family);
// On Windows shm is negotiated by sharing an unguessable token
// over TCP sockets. In theory works on any socket type, in practice
// we need to tell the difference between a local and a remote
// connection. For now we assume everything is local.
// See comments on r.android.com/2951909 .
return true;
#endif
}
inline bool SockShmemSupported(const char* addr) {
return SockShmemSupported(GetSockFamily(addr));
}
// UnixSocketRaw is a basic wrapper around sockets. It exposes wrapper
// methods that take care of most common pitfalls (e.g., marking fd as
// O_CLOEXEC, avoiding SIGPIPE, properly handling partial writes). It is used as
// a building block for the more sophisticated UnixSocket class which depends
// on base::TaskRunner.
class UnixSocketRaw {
public:
// Creates a new unconnected unix socket.
static UnixSocketRaw CreateMayFail(SockFamily family, SockType type);
#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
// Crates a pair of connected sockets.
static std::pair<UnixSocketRaw, UnixSocketRaw> CreatePairPosix(SockFamily,
SockType);
#endif
// Creates an uninitialized unix socket.
UnixSocketRaw();
// Creates a unix socket adopting an existing file descriptor. This is
// typically used to inherit fds from init via environment variables.
UnixSocketRaw(ScopedSocketHandle, SockFamily, SockType);
~UnixSocketRaw() = default;
UnixSocketRaw(UnixSocketRaw&&) noexcept = default;
UnixSocketRaw& operator=(UnixSocketRaw&&) = default;
bool Bind(const std::string& socket_name);
bool Listen();
bool Connect(const std::string& socket_name);
bool SetTxTimeout(uint32_t timeout_ms);
bool SetRxTimeout(uint32_t timeout_ms);
void Shutdown();
void SetBlocking(bool);
void DcheckIsBlocking(bool expected) const; // No-op on release and Win.
void SetRetainOnExec(bool retain);
std::string GetSockAddr() const;
SockType type() const { return type_; }
SockFamily family() const { return family_; }
SocketHandle fd() const { return *fd_; }
explicit operator bool() const { return !!fd_; }
// This is the handle that passed to TaskRunner.AddFileDescriptorWatch().
// On UNIX this is just the socket FD. On Windows, we need to create a
// dedicated event object.
PlatformHandle watch_handle() const {
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
return *event_handle_;
#else
return *fd_;
#endif
}
ScopedSocketHandle ReleaseFd() { return std::move(fd_); }
// |send_fds| and |num_fds| are ignored on Windows.
ssize_t Send(const void* msg,
size_t len,
const int* send_fds = nullptr,
size_t num_fds = 0);
ssize_t SendStr(const std::string& str) {
return Send(str.data(), str.size());
}
// |fd_vec| and |max_files| are ignored on Windows.
ssize_t Receive(void* msg,
size_t len,
ScopedFile* fd_vec = nullptr,
size_t max_files = 0);
#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
// UNIX-specific helpers to deal with SCM_RIGHTS.
// Re-enter sendmsg until all the data has been sent or an error occurs.
// TODO(fmayer): Figure out how to do timeouts here for heapprofd.
ssize_t SendMsgAllPosix(struct msghdr* msg);
// Exposed for testing only.
// Update msghdr so subsequent sendmsg will send data that remains after n
// bytes have already been sent.
static void ShiftMsgHdrPosix(size_t n, struct msghdr* msg);
#endif
private:
UnixSocketRaw(SockFamily, SockType);
UnixSocketRaw(const UnixSocketRaw&) = delete;
UnixSocketRaw& operator=(const UnixSocketRaw&) = delete;
ScopedSocketHandle fd_;
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
ScopedPlatformHandle event_handle_;
#endif
SockFamily family_ = SockFamily::kUnix;
SockType type_ = SockType::kStream;
uint32_t tx_timeout_ms_ = 0;
};
// A non-blocking UNIX domain socket. Allows also to transfer file descriptors.
// None of the methods in this class are blocking.
// The main design goal is making strong guarantees on the EventListener
// callbacks, in order to avoid ending in some undefined state.
// In case of any error it will aggressively just shut down the socket and
// notify the failure with OnConnect(false) or OnDisconnect() depending on the
// state of the socket (see below).
// EventListener callbacks stop happening as soon as the instance is destroyed.
//
// Lifecycle of a client socket:
//
// Connect()
// |
// +------------------+------------------+
// | (success) | (failure or Shutdown())
// V V
// OnConnect(true) OnConnect(false)
// |
// V
// OnDataAvailable()
// |
// V
// OnDisconnect() (failure or shutdown)
//
//
// Lifecycle of a server socket:
//
// Listen() --> returns false in case of errors.
// |
// V
// OnNewIncomingConnection(new_socket)
//
// (|new_socket| inherits the same EventListener)
// |
// V
// OnDataAvailable()
// | (failure or Shutdown())
// V
// OnDisconnect()
class PERFETTO_EXPORT_COMPONENT UnixSocket {
public:
class EventListener {
public:
EventListener() = default;
virtual ~EventListener();
EventListener(const EventListener&) = delete;
EventListener& operator=(const EventListener&) = delete;
EventListener(EventListener&&) noexcept = default;
EventListener& operator=(EventListener&&) noexcept = default;
// After Listen().
// |self| may be null if the connection was not accepted via a listen
// socket.
virtual void OnNewIncomingConnection(
UnixSocket* self,
std::unique_ptr<UnixSocket> new_connection);
// After Connect(), whether successful or not.
virtual void OnConnect(UnixSocket* self, bool connected);
// After a successful Connect() or OnNewIncomingConnection(). Either the
// other endpoint did disconnect or some other error happened.
virtual void OnDisconnect(UnixSocket* self);
// Whenever there is data available to Receive(). Note that spurious FD
// watch events are possible, so it is possible that Receive() soon after
// OnDataAvailable() returns 0 (just ignore those).
virtual void OnDataAvailable(UnixSocket* self);
};
enum class State {
kDisconnected = 0, // Failed connection, peer disconnection or Shutdown().
kConnecting, // Soon after Connect(), before it either succeeds or fails.
kConnected, // After a successful Connect().
kListening // After Listen(), until Shutdown().
};
// Creates a socket and starts listening. If SockFamily::kUnix and
// |socket_name| starts with a '@', an abstract UNIX dmoain socket will be
// created instead of a filesystem-linked UNIX socket (Linux/Android only).
// If SockFamily::kInet, |socket_name| is host:port (e.g., "1.2.3.4:8000").
// If SockFamily::kInet6, |socket_name| is [host]:port (e.g., "[::1]:8000").
// Returns nullptr if the socket creation or bind fails. If listening fails,
// (e.g. if another socket with the same name is already listening) the
// returned socket will have is_listening() == false.
static std::unique_ptr<UnixSocket> Listen(const std::string& socket_name,
EventListener*,
TaskRunner*,
SockFamily,
SockType);
// Attaches to a pre-existing socket. The socket must have been created in
// SOCK_STREAM mode and the caller must have called bind() on it.
static std::unique_ptr<UnixSocket> Listen(ScopedSocketHandle,
EventListener*,
TaskRunner*,
SockFamily,
SockType);
// Creates a Unix domain socket and connects to the listening endpoint.
// Returns always an instance. EventListener::OnConnect(bool success) will
// be called always, whether the connection succeeded or not.
static std::unique_ptr<UnixSocket> Connect(
const std::string& socket_name,
EventListener*,
TaskRunner*,
SockFamily,
SockType,
SockPeerCredMode = SockPeerCredMode::kDefault);
// Constructs a UnixSocket using the given connected socket.
static std::unique_ptr<UnixSocket> AdoptConnected(
ScopedSocketHandle,
EventListener*,
TaskRunner*,
SockFamily,
SockType,
SockPeerCredMode = SockPeerCredMode::kDefault);
UnixSocket(const UnixSocket&) = delete;
UnixSocket& operator=(const UnixSocket&) = delete;
// Cannot be easily moved because of tasks from the FileDescriptorWatch.
UnixSocket(UnixSocket&&) = delete;
UnixSocket& operator=(UnixSocket&&) = delete;
// This class gives the hard guarantee that no callback is called on the
// passed EventListener immediately after the object has been destroyed.
// Any queued callback will be silently dropped.
~UnixSocket();
// Shuts down the current connection, if any. If the socket was Listen()-ing,
// stops listening. The socket goes back to kNotInitialized state, so it can
// be reused with Listen() or Connect().
void Shutdown(bool notify);
void SetTxTimeout(uint32_t timeout_ms) {
PERFETTO_CHECK(sock_raw_.SetTxTimeout(timeout_ms));
}
void SetRxTimeout(uint32_t timeout_ms) {
PERFETTO_CHECK(sock_raw_.SetRxTimeout(timeout_ms));
}
std::string GetSockAddr() const { return sock_raw_.GetSockAddr(); }
// Returns true is the message was queued, false if there was no space in the
// output buffer, in which case the client should retry or give up.
// If any other error happens the socket will be shutdown and
// EventListener::OnDisconnect() will be called.
// If the socket is not connected, Send() will just return false.
// Does not append a null string terminator to msg in any case.
bool Send(const void* msg, size_t len, const int* send_fds, size_t num_fds);
inline bool Send(const void* msg, size_t len, int send_fd = -1) {
if (send_fd != -1)
return Send(msg, len, &send_fd, 1);
return Send(msg, len, nullptr, 0);
}
inline bool SendStr(const std::string& msg) {
return Send(msg.data(), msg.size(), -1);
}
// Returns the number of bytes (<= |len|) written in |msg| or 0 if there
// is no data in the buffer to read or an error occurs (in which case a
// EventListener::OnDisconnect() will follow).
// If the ScopedFile pointer is not null and a FD is received, it moves the
// received FD into that. If a FD is received but the ScopedFile pointer is
// null, the FD will be automatically closed.
size_t Receive(void* msg, size_t len, ScopedFile*, size_t max_files = 1);
inline size_t Receive(void* msg, size_t len) {
return Receive(msg, len, nullptr, 0);
}
// Only for tests. This is slower than Receive() as it requires a heap
// allocation and a copy for the std::string. Guarantees that the returned
// string is null terminated even if the underlying message sent by the peer
// is not.
std::string ReceiveString(size_t max_length = 1024);
bool is_connected() const { return state_ == State::kConnected; }
bool is_listening() const { return state_ == State::kListening; }
SocketHandle fd() const { return sock_raw_.fd(); }
SockFamily family() const { return sock_raw_.family(); }
// User ID of the peer, as returned by the kernel. If the client disconnects
// and the socket goes into the kDisconnected state, it retains the uid of
// the last peer.
#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) && \
!PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
uid_t peer_uid_posix(bool skip_check_for_testing = false) const {
PERFETTO_DCHECK((!is_listening() && peer_uid_ != kInvalidUid) ||
skip_check_for_testing);
return peer_uid_;
}
#endif
#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
// Process ID of the peer, as returned by the kernel. If the client
// disconnects and the socket goes into the kDisconnected state, it
// retains the pid of the last peer.
//
// This is only available on Linux / Android.
pid_t peer_pid_linux(bool skip_check_for_testing = false) const {
PERFETTO_DCHECK((!is_listening() && peer_pid_ != kInvalidPid) ||
skip_check_for_testing);
return peer_pid_;
}
#endif
// This makes the UnixSocket unusable.
UnixSocketRaw ReleaseSocket();
private:
UnixSocket(EventListener*,
TaskRunner*,
SockFamily,
SockType,
SockPeerCredMode);
UnixSocket(EventListener*,
TaskRunner*,
ScopedSocketHandle,
State,
SockFamily,
SockType,
SockPeerCredMode);
// Called once by the corresponding public static factory methods.
void DoConnect(const std::string& socket_name);
#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
void ReadPeerCredentialsPosix();
#endif
void OnEvent();
void NotifyConnectionState(bool success);
UnixSocketRaw sock_raw_;
State state_ = State::kDisconnected;
SockPeerCredMode peer_cred_mode_ = SockPeerCredMode::kDefault;
#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) && \
!PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
uid_t peer_uid_ = kInvalidUid;
#endif
#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
pid_t peer_pid_ = kInvalidPid;
#endif
EventListener* const event_listener_;
TaskRunner* const task_runner_;
WeakPtrFactory<UnixSocket> weak_ptr_factory_; // Keep last.
};
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_UNIX_SOCKET_H_

View file

@ -0,0 +1,133 @@
/*
* Copyright (C) 2017 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 INCLUDE_PERFETTO_EXT_BASE_UNIX_TASK_RUNNER_H_
#define INCLUDE_PERFETTO_EXT_BASE_UNIX_TASK_RUNNER_H_
#include "perfetto/base/build_config.h"
#include "perfetto/base/task_runner.h"
#include "perfetto/base/thread_utils.h"
#include "perfetto/base/time.h"
#include "perfetto/ext/base/event_fd.h"
#include "perfetto/ext/base/scoped_file.h"
#include "perfetto/ext/base/thread_checker.h"
#include <chrono>
#include <deque>
#include <map>
#include <mutex>
#include <vector>
#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
#include <poll.h>
#endif
namespace perfetto {
namespace base {
// Runs a task runner on the current thread.
//
// Implementation note: we currently assume (and enforce in debug builds) that
// Run() is called from the thread that constructed the UnixTaskRunner. This is
// not strictly necessary, and we could instead track the thread that invokes
// Run(). However, a related property that *might* be important to enforce is
// that the destructor runs on the task-running thread. Otherwise, if there are
// still-pending tasks at the time of destruction, we would destroy those
// outside of the task thread (which might be unexpected to the caller). On the
// other hand, the std::function task interface discourages use of any
// resource-owning tasks (as the callable needs to be copyable), so this might
// not be important in practice.
//
// TODO(rsavitski): consider adding a thread-check in the destructor, after
// auditing existing usages.
// TODO(primiano): rename this to TaskRunnerImpl. The "Unix" part is misleading
// now as it supports also Windows.
class UnixTaskRunner : public TaskRunner {
public:
UnixTaskRunner();
~UnixTaskRunner() override;
// Start executing tasks. Doesn't return until Quit() is called. Run() may be
// called multiple times on the same task runner.
void Run();
void Quit();
// Checks whether there are any pending immediate tasks to run. Note that
// delayed tasks don't count even if they are due to run.
bool IsIdleForTesting();
// TaskRunner implementation:
void PostTask(std::function<void()>) override;
void PostDelayedTask(std::function<void()>, uint32_t delay_ms) override;
void AddFileDescriptorWatch(PlatformHandle, std::function<void()>) override;
void RemoveFileDescriptorWatch(PlatformHandle) override;
bool RunsTasksOnCurrentThread() const override;
// Returns true if the task runner is quitting, or has quit and hasn't been
// restarted since. Exposed primarily for ThreadTaskRunner, not necessary for
// normal use of this class.
bool QuitCalled();
private:
void WakeUp();
void UpdateWatchTasksLocked();
int GetDelayMsToNextTaskLocked() const;
void RunImmediateAndDelayedTask();
void PostFileDescriptorWatches(uint64_t windows_wait_result);
void RunFileDescriptorWatch(PlatformHandle);
ThreadChecker thread_checker_;
PlatformThreadId created_thread_id_ = GetThreadId();
EventFd event_;
// The array of fds/handles passed to poll(2) / WaitForMultipleObjects().
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
std::vector<PlatformHandle> poll_fds_;
#else
std::vector<struct pollfd> poll_fds_;
#endif
// --- Begin lock-protected members ---
std::mutex lock_;
std::deque<std::function<void()>> immediate_tasks_;
std::multimap<TimeMillis, std::function<void()>> delayed_tasks_;
bool quit_ = false;
struct WatchTask {
std::function<void()> callback;
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
// On UNIX systems we make the FD number negative in |poll_fds_| to avoid
// polling it again until the queued task runs. On Windows we can't do that.
// Instead we keep track of its state here.
bool pending = false;
#else
size_t poll_fd_index; // Index into |poll_fds_|.
#endif
};
std::map<PlatformHandle, WatchTask> watch_tasks_;
bool watch_tasks_changed_ = false;
// --- End lock-protected members ---
};
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_UNIX_TASK_RUNNER_H_

View file

@ -0,0 +1,196 @@
/*
* Copyright (C) 2017 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 INCLUDE_PERFETTO_EXT_BASE_UTILS_H_
#define INCLUDE_PERFETTO_EXT_BASE_UTILS_H_
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <atomic>
#include <functional>
#include <memory>
#include <string>
#include "perfetto/base/build_config.h"
#include "perfetto/base/compiler.h"
#include "perfetto/ext/base/sys_types.h"
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
// Even if Windows has errno.h, the all syscall-restart behavior does not apply.
// Trying to handle EINTR can cause more harm than good if errno is left stale.
// Chromium does the same.
#define PERFETTO_EINTR(x) (x)
#else
#define PERFETTO_EINTR(x) \
([&] { \
decltype(x) eintr_wrapper_result; \
do { \
eintr_wrapper_result = (x); \
} while (eintr_wrapper_result == -1 && errno == EINTR); \
return eintr_wrapper_result; \
}())
#endif
namespace perfetto {
namespace base {
namespace internal {
extern std::atomic<uint32_t> g_cached_page_size;
uint32_t GetSysPageSizeSlowpath();
} // namespace internal
// Returns the system's page size. Use this when dealing with mmap, madvise and
// similar mm-related syscalls.
// This function might be called in hot paths. Avoid calling getpagesize() all
// the times, in many implementations getpagesize() calls sysconf() which is
// not cheap.
inline uint32_t GetSysPageSize() {
const uint32_t page_size =
internal::g_cached_page_size.load(std::memory_order_relaxed);
return page_size != 0 ? page_size : internal::GetSysPageSizeSlowpath();
}
template <typename T, size_t TSize>
constexpr size_t ArraySize(const T (&)[TSize]) {
return TSize;
}
// Function object which invokes 'free' on its parameter, which must be
// a pointer. Can be used to store malloc-allocated pointers in std::unique_ptr:
//
// std::unique_ptr<int, base::FreeDeleter> foo_ptr(
// static_cast<int*>(malloc(sizeof(int))));
struct FreeDeleter {
inline void operator()(void* ptr) const { free(ptr); }
};
template <typename T>
constexpr T AssumeLittleEndian(T value) {
#if !PERFETTO_IS_LITTLE_ENDIAN()
static_assert(false, "Unimplemented on big-endian archs");
#endif
return value;
}
// Round up |size| to a multiple of |alignment| (must be a power of two).
inline constexpr size_t AlignUp(size_t size, size_t alignment) {
return (size + alignment - 1) & ~(alignment - 1);
}
// TODO(primiano): clean this up and move all existing usages to the constexpr
// version above.
template <size_t alignment>
constexpr size_t AlignUp(size_t size) {
static_assert((alignment & (alignment - 1)) == 0, "alignment must be a pow2");
return AlignUp(size, alignment);
}
inline bool IsAgain(int err) {
return err == EAGAIN || err == EWOULDBLOCK;
}
// setenv(2)-equivalent. Deals with Windows vs Posix discrepancies.
void SetEnv(const std::string& key, const std::string& value);
// unsetenv(2)-equivalent. Deals with Windows vs Posix discrepancies.
void UnsetEnv(const std::string& key);
// Calls mallopt(M_PURGE, 0) on Android. Does nothing on other platforms.
// This forces the allocator to release freed memory. This is used to work
// around various Scudo inefficiencies. See b/170217718.
void MaybeReleaseAllocatorMemToOS();
// geteuid() on POSIX OSes, returns 0 on Windows (See comment in utils.cc).
uid_t GetCurrentUserId();
// Forks the process.
// Parent: prints the PID of the child, calls |parent_cb| and exits from the
// process with its return value.
// Child: redirects stdio onto /dev/null, chdirs into / and returns.
void Daemonize(std::function<int()> parent_cb);
// Returns the path of the current executable, e.g. /foo/bar/exe.
std::string GetCurExecutablePath();
// Returns the directory where the current executable lives in, e.g. /foo/bar.
// This is independent of cwd().
std::string GetCurExecutableDir();
// Memory returned by AlignedAlloc() must be freed via AlignedFree() not just
// free. It makes a difference on Windows where _aligned_malloc() and
// _aligned_free() must be paired.
// Prefer using the AlignedAllocTyped() below which takes care of the pairing.
void* AlignedAlloc(size_t alignment, size_t size);
void AlignedFree(void*);
// A RAII version of the above, which takes care of pairing Aligned{Alloc,Free}.
template <typename T>
struct AlignedDeleter {
inline void operator()(T* ptr) const { AlignedFree(ptr); }
};
// The remove_extent<T> here and below is to allow defining unique_ptr<T[]>.
// As per https://en.cppreference.com/w/cpp/memory/unique_ptr the Deleter takes
// always a T*, not a T[]*.
template <typename T>
using AlignedUniquePtr =
std::unique_ptr<T, AlignedDeleter<typename std::remove_extent<T>::type>>;
template <typename T>
AlignedUniquePtr<T> AlignedAllocTyped(size_t n_membs) {
using TU = typename std::remove_extent<T>::type;
return AlignedUniquePtr<T>(
static_cast<TU*>(AlignedAlloc(alignof(TU), sizeof(TU) * n_membs)));
}
// A RAII wrapper to invoke a function when leaving a function/scope.
template <typename Func>
class OnScopeExitWrapper {
public:
explicit OnScopeExitWrapper(Func f) : f_(std::move(f)), active_(true) {}
OnScopeExitWrapper(OnScopeExitWrapper&& other) noexcept
: f_(std::move(other.f_)), active_(other.active_) {
other.active_ = false;
}
~OnScopeExitWrapper() {
if (active_)
f_();
}
private:
Func f_;
bool active_;
};
template <typename Func>
PERFETTO_WARN_UNUSED_RESULT OnScopeExitWrapper<Func> OnScopeExit(Func f) {
return OnScopeExitWrapper<Func>(std::move(f));
}
// Returns a xxd-style hex dump (hex + ascii chars) of the input data.
std::string HexDump(const void* data, size_t len, size_t bytes_per_line = 16);
inline std::string HexDump(const std::string& data,
size_t bytes_per_line = 16) {
return HexDump(data.data(), data.size(), bytes_per_line);
}
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_UTILS_H_

View file

@ -0,0 +1,75 @@
/*
* Copyright (C) 2019 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 INCLUDE_PERFETTO_EXT_BASE_UUID_H_
#define INCLUDE_PERFETTO_EXT_BASE_UUID_H_
#include <string.h>
#include <array>
#include <cstdint>
#include <optional>
#include <string>
namespace perfetto {
namespace base {
class Uuid {
public:
explicit Uuid(const std::string& s);
explicit Uuid(int64_t lsb, int64_t msb);
Uuid();
std::array<uint8_t, 16>* data() { return &data_; }
const std::array<uint8_t, 16>* data() const { return &data_; }
bool operator==(const Uuid& other) const { return data_ == other.data_; }
bool operator!=(const Uuid& other) const { return !(*this == other); }
explicit operator bool() const { return *this != Uuid(); }
int64_t msb() const {
int64_t result;
memcpy(&result, data_.data() + 8, 8);
return result;
}
int64_t lsb() const {
int64_t result;
memcpy(&result, data_.data(), 8);
return result;
}
void set_lsb_msb(int64_t lsb, int64_t msb) {
set_lsb(lsb);
set_msb(msb);
}
void set_msb(int64_t msb) { memcpy(data_.data() + 8, &msb, 8); }
void set_lsb(int64_t lsb) { memcpy(data_.data(), &lsb, 8); }
std::string ToString() const;
std::string ToPrettyString() const;
private:
std::array<uint8_t, 16> data_{};
};
Uuid Uuidv4();
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_UUID_H_

View file

@ -0,0 +1,47 @@
/*
* 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 INCLUDE_PERFETTO_EXT_BASE_VERSION_H_
#define INCLUDE_PERFETTO_EXT_BASE_VERSION_H_
namespace perfetto {
namespace base {
// The returned pointer is a static string and safe to pass around.
// Returns a human readable string currently of the approximate form:
// Perfetto v42.1-deadbeef0 (deadbeef03c641e4b4ea9cf38e9b5696670175a9)
// However you should not depend on the format of this string.
// It maybe not be possible to determine the version. In which case the
// string will be of the approximate form:
// Perfetto v0.0 (unknown)
const char* GetVersionString();
// The returned pointer is a static string and safe to pass around.
// Returns the short code used to identity the version:
// v42.1-deadbeef0
// It maybe not be possible to determine the version. In which case
// this returns nullptr.
// This can be compared with equality to other
// version codes to detect matched builds (for example to see if
// trace_processor_shell and the UI were built at the same revision)
// but you should not attempt to parse it as the format may change
// without warning.
const char* GetVersionCode();
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_VERSION_H_

View file

@ -0,0 +1,50 @@
/*
* Copyright (C) 2019 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 INCLUDE_PERFETTO_EXT_BASE_WAITABLE_EVENT_H_
#define INCLUDE_PERFETTO_EXT_BASE_WAITABLE_EVENT_H_
#include <condition_variable>
#include <mutex>
namespace perfetto {
namespace base {
// A waitable event for cross-thread synchronization.
// All methods on this class can be called from any thread.
class WaitableEvent {
public:
WaitableEvent();
~WaitableEvent();
WaitableEvent(const WaitableEvent&) = delete;
WaitableEvent operator=(const WaitableEvent&) = delete;
// Synchronously block until the event is notified `notification` times.
void Wait(uint64_t notifications = 1);
// Signal the event, waking up blocked waiters.
void Notify();
private:
std::mutex mutex_;
std::condition_variable event_;
uint64_t notifications_ = 0;
};
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_WAITABLE_EVENT_H_

View file

@ -0,0 +1,79 @@
/*
* Copyright (C) 2018 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 INCLUDE_PERFETTO_EXT_BASE_WATCHDOG_H_
#define INCLUDE_PERFETTO_EXT_BASE_WATCHDOG_H_
#include <functional>
#include "perfetto/base/build_config.h"
// The POSIX watchdog is only supported on Linux and Android in non-embedder
// builds.
#if PERFETTO_BUILDFLAG(PERFETTO_WATCHDOG)
#include "perfetto/ext/base/watchdog_posix.h"
#else
#include "perfetto/ext/base/watchdog_noop.h"
#endif
namespace perfetto {
namespace base {
// Used only to add more details to crash reporting.
enum class WatchdogCrashReason {
kUnspecified = 0,
kCpuGuardrail = 1,
kMemGuardrail = 2,
kTaskRunnerHung = 3,
kTraceDidntStop = 4,
};
// Make the limits more relaxed on desktop, where multi-GB traces are likely.
// Multi-GB traces can take bursts of cpu time to write into disk at the end of
// the trace.
#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
constexpr uint32_t kWatchdogDefaultCpuLimit = 75;
constexpr uint32_t kWatchdogDefaultCpuWindow = 5 * 60 * 1000; // 5 minutes.
#else
constexpr uint32_t kWatchdogDefaultCpuLimit = 90;
constexpr uint32_t kWatchdogDefaultCpuWindow = 10 * 60 * 1000; // 10 minutes.
#endif
// The default memory margin we give to our processes. This is used as as a
// constant to put on top of the trace buffers.
constexpr uint64_t kWatchdogDefaultMemorySlack = 32 * 1024 * 1024; // 32 MiB.
constexpr uint32_t kWatchdogDefaultMemoryWindow = 30 * 1000; // 30 seconds.
inline void RunTaskWithWatchdogGuard(const std::function<void()>& task) {
// Maximum time a single task can take in a TaskRunner before the
// program suicides.
constexpr int64_t kWatchdogMillis = 30000; // 30s
Watchdog::Timer handle = base::Watchdog::GetInstance()->CreateFatalTimer(
kWatchdogMillis, WatchdogCrashReason::kTaskRunnerHung);
task();
// Suppress unused variable warnings in the client library amalgamated build.
(void)kWatchdogDefaultCpuLimit;
(void)kWatchdogDefaultCpuWindow;
(void)kWatchdogDefaultMemorySlack;
(void)kWatchdogDefaultMemoryWindow;
}
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_WATCHDOG_H_

View file

@ -0,0 +1,51 @@
/*
* Copyright (C) 2018 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 INCLUDE_PERFETTO_EXT_BASE_WATCHDOG_NOOP_H_
#define INCLUDE_PERFETTO_EXT_BASE_WATCHDOG_NOOP_H_
#include <stdint.h>
namespace perfetto {
namespace base {
enum class WatchdogCrashReason; // Defined in watchdog.h.
class Watchdog {
public:
class Timer {
public:
// Define an empty dtor to avoid "unused variable" errors on the call site.
Timer() {}
Timer(const Timer&) {}
~Timer() {}
};
static Watchdog* GetInstance() {
static Watchdog* watchdog = new Watchdog();
return watchdog;
}
Timer CreateFatalTimer(uint32_t /*ms*/, WatchdogCrashReason) {
return Timer();
}
void Start() {}
void SetMemoryLimit(uint64_t /*bytes*/, uint32_t /*window_ms*/) {}
void SetCpuLimit(uint32_t /*percentage*/, uint32_t /*window_ms*/) {}
};
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_WATCHDOG_NOOP_H_

View file

@ -0,0 +1,202 @@
/*
* Copyright (C) 2018 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 INCLUDE_PERFETTO_EXT_BASE_WATCHDOG_POSIX_H_
#define INCLUDE_PERFETTO_EXT_BASE_WATCHDOG_POSIX_H_
#include "perfetto/base/time.h"
#include "perfetto/ext/base/scoped_file.h"
#include <atomic>
#include <mutex>
#include <thread>
#include <vector>
namespace perfetto {
namespace base {
enum class WatchdogCrashReason; // Defined in watchdog.h.
struct ProcStat {
unsigned long int utime = 0l;
unsigned long int stime = 0l;
long int rss_pages = -1l;
};
bool ReadProcStat(int fd, ProcStat* out);
// Ensures that the calling program does not exceed certain hard limits on
// resource usage e.g. time, memory and CPU. If exceeded, the program is
// crashed.
class Watchdog {
public:
struct TimerData {
TimeMillis deadline{}; // Absolute deadline, CLOCK_MONOTONIC.
int thread_id = 0; // The tid we'll send a SIGABRT to on expiry.
WatchdogCrashReason crash_reason{}; // Becomes a crash key.
TimerData() = default;
TimerData(TimeMillis d, int t) : deadline(d), thread_id(t) {}
bool operator<(const TimerData& x) const {
return std::tie(deadline, thread_id) < std::tie(x.deadline, x.thread_id);
}
bool operator==(const TimerData& x) const {
return std::tie(deadline, thread_id) == std::tie(x.deadline, x.thread_id);
}
};
// Handle to the timer set to crash the program. If the handle is dropped,
// the timer is removed so the program does not crash.
class Timer {
public:
~Timer();
Timer(Timer&&) noexcept;
private:
friend class Watchdog;
explicit Timer(Watchdog*, uint32_t ms, WatchdogCrashReason);
Timer(const Timer&) = delete;
Timer& operator=(const Timer&) = delete;
// In production this is always Watchdog::GetInstance(), which is long
// lived. However unittests use a non-global instance.
Watchdog* watchdog_ = nullptr;
TimerData timer_data_;
};
virtual ~Watchdog();
static Watchdog* GetInstance();
// Sets a timer which will crash the program in |ms| milliseconds if the
// returned handle is not destroyed before this point.
// WatchdogCrashReason is used only to set a crash key in the case of a crash,
// to disambiguate different timer types.
Timer CreateFatalTimer(uint32_t ms, WatchdogCrashReason);
// Starts the watchdog thread which monitors the memory and CPU usage
// of the program.
void Start();
// Sets a limit on the memory (defined as the RSS) used by the program
// averaged over the last |window_ms| milliseconds. If |kb| is 0, any
// existing limit is removed.
// Note: |window_ms| has to be a multiple of |polling_interval_ms_|.
void SetMemoryLimit(uint64_t bytes, uint32_t window_ms);
// Sets a limit on the CPU usage used by the program averaged over the last
// |window_ms| milliseconds. If |percentage| is 0, any existing limit is
// removed.
// Note: |window_ms| has to be a multiple of |polling_interval_ms_|.
void SetCpuLimit(uint32_t percentage, uint32_t window_ms);
private:
// Represents a ring buffer in which integer values can be stored.
class WindowedInterval {
public:
// Pushes a new value into a ring buffer wrapping if necessary and returns
// whether the ring buffer is full.
bool Push(uint64_t sample);
// Returns the mean of the values in the buffer.
double Mean() const;
// Clears the ring buffer while keeping the existing size.
void Clear();
// Resets the size of the buffer as well as clearing it.
void Reset(size_t new_size);
// Gets the oldest value inserted in the buffer. The buffer must be full
// (i.e. Push returned true) before this method can be called.
uint64_t OldestWhenFull() const {
PERFETTO_CHECK(filled_);
return buffer_[position_];
}
// Gets the newest value inserted in the buffer. The buffer must be full
// (i.e. Push returned true) before this method can be called.
uint64_t NewestWhenFull() const {
PERFETTO_CHECK(filled_);
return buffer_[(position_ + size_ - 1) % size_];
}
// Returns the size of the ring buffer.
size_t size() const { return size_; }
private:
bool filled_ = false;
size_t position_ = 0;
size_t size_ = 0;
std::unique_ptr<uint64_t[]> buffer_;
};
Watchdog(const Watchdog&) = delete;
Watchdog& operator=(const Watchdog&) = delete;
Watchdog(Watchdog&&) = delete;
Watchdog& operator=(Watchdog&&) = delete;
// Main method for the watchdog thread.
void ThreadMain();
// Check each type of resource every |polling_interval_ms_| miillis.
// Returns true if the threshold is exceeded and the process should be killed.
bool CheckMemory_Locked(uint64_t rss_bytes);
bool CheckCpu_Locked(uint64_t cpu_time);
void AddFatalTimer(TimerData);
void RemoveFatalTimer(TimerData);
void RearmTimerFd_Locked();
void SerializeLogsAndKillThread(int tid, WatchdogCrashReason);
// Computes the time interval spanned by a given ring buffer with respect
// to |polling_interval_ms_|.
uint32_t WindowTimeForRingBuffer(const WindowedInterval& window);
const uint32_t polling_interval_ms_;
std::atomic<bool> enabled_{false};
std::thread thread_;
ScopedPlatformHandle timer_fd_;
// --- Begin lock-protected members ---
std::mutex mutex_;
uint64_t memory_limit_bytes_ = 0;
WindowedInterval memory_window_bytes_;
uint32_t cpu_limit_percentage_ = 0;
WindowedInterval cpu_window_time_ticks_;
// Outstanding timers created via CreateFatalTimer() and not yet destroyed.
// The vector is not sorted. In most cases there are only 1-2 timers, we can
// afford O(N) operations.
// All the timers in the list share the same |timer_fd_|, which is keeped
// armed on the min(timers_) through RearmTimerFd_Locked().
std::vector<TimerData> timers_;
// --- End lock-protected members ---
protected:
// Protected for testing.
explicit Watchdog(uint32_t polling_interval_ms);
bool disable_kill_failsafe_for_testing_ = false;
};
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_WATCHDOG_POSIX_H_

View file

@ -0,0 +1,120 @@
/*
* Copyright (C) 2017 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 INCLUDE_PERFETTO_EXT_BASE_WEAK_PTR_H_
#define INCLUDE_PERFETTO_EXT_BASE_WEAK_PTR_H_
#include "perfetto/ext/base/thread_checker.h"
#include <memory>
namespace perfetto {
namespace base {
// A simple WeakPtr for single-threaded cases.
// Generally keep the WeakPtrFactory as last fields in classes: it makes the
// WeakPtr(s) invalidate as first thing in the class dtor.
// Usage:
// class MyClass {
// MyClass() : weak_factory_(this) {}
// WeakPtr<MyClass> GetWeakPtr() { return weak_factory_.GetWeakPtr(); }
//
// private:
// WeakPtrFactory<MyClass> weak_factory_;
// }
//
// int main() {
// std::unique_ptr<MyClass> foo(new MyClass);
// auto wptr = foo.GetWeakPtr();
// ASSERT_TRUE(wptr);
// ASSERT_EQ(foo.get(), wptr->get());
// foo.reset();
// ASSERT_FALSE(wptr);
// ASSERT_EQ(nullptr, wptr->get());
// }
template <typename T>
class WeakPtrFactory; // Forward declaration, defined below.
template <typename T>
class WeakPtr {
public:
WeakPtr() {}
WeakPtr(const WeakPtr&) = default;
WeakPtr& operator=(const WeakPtr&) = default;
WeakPtr(WeakPtr&&) = default;
WeakPtr& operator=(WeakPtr&&) = default;
T* get() const {
PERFETTO_DCHECK_THREAD(thread_checker);
return handle_ ? *handle_.get() : nullptr;
}
T* operator->() const { return get(); }
T& operator*() const { return *get(); }
explicit operator bool() const { return !!get(); }
private:
friend class WeakPtrFactory<T>;
explicit WeakPtr(const std::shared_ptr<T*>& handle) : handle_(handle) {}
std::shared_ptr<T*> handle_;
PERFETTO_THREAD_CHECKER(thread_checker)
};
template <typename T>
class WeakPtrFactory {
public:
explicit WeakPtrFactory(T* owner) : weak_ptr_(std::make_shared<T*>(owner)) {
PERFETTO_DCHECK_THREAD(thread_checker);
}
~WeakPtrFactory() {
PERFETTO_DCHECK_THREAD(thread_checker);
*(weak_ptr_.handle_.get()) = nullptr;
}
// Can be safely called on any thread, since it simply copies |weak_ptr_|.
// Note that any accesses to the returned pointer need to be made on the
// thread that created/reset the factory.
WeakPtr<T> GetWeakPtr() const { return weak_ptr_; }
// Reset the factory to a new owner & thread. May only be called before any
// weak pointers were passed out. Future weak pointers will be valid on the
// calling thread.
void Reset(T* owner) {
// Reset thread checker to current thread.
PERFETTO_DETACH_FROM_THREAD(thread_checker);
PERFETTO_DCHECK_THREAD(thread_checker);
// We should not have passed out any weak pointers yet at this point.
PERFETTO_DCHECK(weak_ptr_.handle_.use_count() == 1);
weak_ptr_ = WeakPtr<T>(std::make_shared<T*>(owner));
}
private:
WeakPtrFactory(const WeakPtrFactory&) = delete;
WeakPtrFactory& operator=(const WeakPtrFactory&) = delete;
WeakPtr<T> weak_ptr_;
PERFETTO_THREAD_CHECKER(thread_checker)
};
} // namespace base
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_BASE_WEAK_PTR_H_

View file

@ -0,0 +1,32 @@
# Copyright (C) 2017 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.
source_set("ipc") {
public_deps = [
"../../protozero",
"../base",
]
sources = [
"async_result.h",
"basic_types.h",
"client.h",
"client_info.h",
"codegen_helpers.h",
"deferred.h",
"host.h",
"service.h",
"service_descriptor.h",
"service_proxy.h",
]
}

View file

@ -0,0 +1,74 @@
/*
* Copyright (C) 2017 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 INCLUDE_PERFETTO_EXT_IPC_ASYNC_RESULT_H_
#define INCLUDE_PERFETTO_EXT_IPC_ASYNC_RESULT_H_
#include <memory>
#include <type_traits>
#include <utility>
#include "perfetto/ext/ipc/basic_types.h"
namespace perfetto {
namespace ipc {
// Wraps the result of an asynchronous invocation. This is the equivalent of a
// std::pair<unique_ptr<T>, bool> with syntactic sugar. It is used as callback
// argument by Deferred<T>. T is a ProtoMessage subclass (i.e. generated .pb.h).
template <typename T>
class AsyncResult {
public:
static AsyncResult Create() {
return AsyncResult(std::unique_ptr<T>(new T()));
}
AsyncResult(std::unique_ptr<T> msg = nullptr,
bool has_more = false,
int fd = -1)
: msg_(std::move(msg)), has_more_(has_more), fd_(fd) {
static_assert(std::is_base_of<ProtoMessage, T>::value, "T->ProtoMessage");
}
AsyncResult(AsyncResult&&) noexcept = default;
AsyncResult& operator=(AsyncResult&&) = default;
bool success() const { return !!msg_; }
explicit operator bool() const { return success(); }
bool has_more() const { return has_more_; }
void set_has_more(bool has_more) { has_more_ = has_more; }
void set_msg(std::unique_ptr<T> msg) { msg_ = std::move(msg); }
T* release_msg() { return msg_.release(); }
T* operator->() { return msg_.get(); }
T& operator*() { return *msg_; }
void set_fd(int fd) { fd_ = fd; }
int fd() const { return fd_; }
private:
std::unique_ptr<T> msg_;
bool has_more_ = false;
// Optional. Only for messages that convey a file descriptor, for sharing
// memory across processes.
int fd_ = -1;
};
} // namespace ipc
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_IPC_ASYNC_RESULT_H_

View file

@ -0,0 +1,55 @@
/*
* Copyright (C) 2017 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 INCLUDE_PERFETTO_EXT_IPC_BASIC_TYPES_H_
#define INCLUDE_PERFETTO_EXT_IPC_BASIC_TYPES_H_
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
#include "perfetto/ext/base/utils.h"
#include "perfetto/protozero/cpp_message_obj.h"
namespace perfetto {
namespace ipc {
using ProtoMessage = ::protozero::CppMessageObj;
using ServiceID = uint32_t;
using MethodID = uint32_t;
using ClientID = uint64_t;
using RequestID = uint64_t;
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
// AF_UNIX on Windows is supported only on Windows 10 from build 17063.
// Also it doesn't bring major advantages compared to a TCP socket.
// See go/perfetto-win .
constexpr bool kUseTCPSocket = true;
#else
// Android, Linux, Mac, Fuchsia use local sockets.
constexpr bool kUseTCPSocket = false;
#endif
// This determines the maximum size allowed for an IPC message. Trying to send
// or receive a larger message will hit DCHECK(s) and auto-disconnect.
constexpr size_t kIPCBufferSize = 128 * 1024;
constexpr uid_t kInvalidUid = static_cast<uid_t>(-1);
} // namespace ipc
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_IPC_BASIC_TYPES_H_

View file

@ -0,0 +1,89 @@
/*
* Copyright (C) 2017 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 INCLUDE_PERFETTO_EXT_IPC_CLIENT_H_
#define INCLUDE_PERFETTO_EXT_IPC_CLIENT_H_
#include <functional>
#include <memory>
#include "perfetto/ext/base/scoped_file.h"
#include "perfetto/ext/base/unix_socket.h"
#include "perfetto/ext/base/weak_ptr.h"
#include "perfetto/ext/ipc/basic_types.h"
namespace perfetto {
namespace base {
class TaskRunner;
} // namespace base
namespace ipc {
class ServiceProxy;
// The client-side class that talks to the host over the socket and multiplexes
// requests coming from the various autogenerated ServiceProxy stubs.
// This is meant to be used by the user code as follows:
// auto client = Client::CreateInstance("socket_name", task_runner);
// std::unique_ptr<GreeterService> svc(new GreeterService());
// client.BindService(svc);
// svc.OnConnect([] () {
// svc.SayHello(..., ...);
// });
class Client {
public:
// struct ConnArgs is used for creating a client in 2 connection modes:
// 1. Connect using a socket name with the option to retry the connection on
// connection failure.
// 2. Adopt a connected socket.
struct ConnArgs {
ConnArgs(const char* sock_name, bool sock_retry)
: socket_name(sock_name), retry(sock_retry) {}
explicit ConnArgs(base::ScopedSocketHandle sock_fd)
: socket_fd(std::move(sock_fd)) {}
// Disallow copy. Only supports move.
ConnArgs(const ConnArgs& other) = delete;
ConnArgs(ConnArgs&& other) = default;
base::ScopedSocketHandle socket_fd;
const char* socket_name = nullptr;
bool retry = false; // Only for connecting with |socket_name|.
std::function<int(void)> receive_shmem_fd_cb_fuchsia;
};
static std::unique_ptr<Client> CreateInstance(ConnArgs, base::TaskRunner*);
virtual ~Client();
virtual void BindService(base::WeakPtr<ServiceProxy>) = 0;
// There is no need to call this method explicitly. Destroying the
// ServiceProxy instance is sufficient and will automatically unbind it. This
// method is exposed only for the ServiceProxy destructor.
virtual void UnbindService(ServiceID) = 0;
// Returns (with move semantics) the last file descriptor received on the IPC
// channel. No buffering is performed: if a service sends two file descriptors
// and the caller doesn't read them immediately, the first one will be
// automatically closed when the second is received (and will hit a DCHECK in
// debug builds).
virtual base::ScopedFile TakeReceivedFD() = 0;
};
} // namespace ipc
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_IPC_CLIENT_H_

View file

@ -0,0 +1,77 @@
/*
* Copyright (C) 2017 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 INCLUDE_PERFETTO_EXT_IPC_CLIENT_INFO_H_
#define INCLUDE_PERFETTO_EXT_IPC_CLIENT_INFO_H_
#include "perfetto/base/logging.h"
#include "perfetto/ext/base/sys_types.h"
#include "perfetto/ext/base/utils.h"
#include "perfetto/ext/ipc/basic_types.h"
namespace perfetto {
namespace ipc {
// Passed to Service(s) to identify remote clients.
class ClientInfo {
public:
ClientInfo() = default;
ClientInfo(ClientID client_id,
uid_t uid,
pid_t pid,
base::MachineID machine_id)
: client_id_(client_id), uid_(uid), pid_(pid), machine_id_(machine_id) {}
bool operator==(const ClientInfo& other) const {
return std::tie(client_id_, uid_, pid_, machine_id_) ==
std::tie(other.client_id_, other.uid_, other.pid_,
other.machine_id_);
}
bool operator!=(const ClientInfo& other) const { return !(*this == other); }
// For map<> and other sorted containers.
bool operator<(const ClientInfo& other) const {
PERFETTO_DCHECK(client_id_ != other.client_id_ || *this == other);
return client_id_ < other.client_id_;
}
bool is_valid() const { return client_id_ != 0; }
// A monotonic counter.
ClientID client_id() const { return client_id_; }
// Posix User ID. Comes from the kernel, can be trusted.
uid_t uid() const { return uid_; }
// Posix process ID. Comes from the kernel and can be trusted.
int32_t pid() const { return pid_; }
// An integral ID that identifies the machine the client is on.
base::MachineID machine_id() const { return machine_id_; }
private:
ClientID client_id_ = 0;
// The following fields are emitted to trace packets and should be kept in
// sync with perfetto::ClientIdentity.
uid_t uid_ = kInvalidUid;
pid_t pid_ = base::kInvalidPid;
base::MachineID machine_id_ = base::kDefaultMachineID;
};
} // namespace ipc
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_IPC_CLIENT_INFO_H_

View file

@ -0,0 +1,54 @@
/*
* Copyright (C) 2017 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.
*/
// This file is only meant to be included in autogenerated .cc files.
#ifndef INCLUDE_PERFETTO_EXT_IPC_CODEGEN_HELPERS_H_
#define INCLUDE_PERFETTO_EXT_IPC_CODEGEN_HELPERS_H_
#include <memory>
#include "perfetto/ext/ipc/basic_types.h"
#include "perfetto/ext/ipc/deferred.h"
#include "perfetto/ext/ipc/service.h"
// A templated protobuf message decoder. Returns nullptr in case of failure.
template <typename T>
::std::unique_ptr<::perfetto::ipc::ProtoMessage> _IPC_Decoder(
const std::string& proto_data) {
::std::unique_ptr<::perfetto::ipc::ProtoMessage> msg(new T());
if (msg->ParseFromString(proto_data))
return msg;
return nullptr;
}
// Templated method dispatcher. Used to obtain a function pointer to a given
// IPC method (Method) of a given service (TSvc) that can be invoked by the
// host-side machinery starting from a generic Service pointer and a generic
// ProtoMessage request argument.
template <typename TSvc, // Type of the actual Service subclass.
typename TReq, // Type of the request argument.
typename TReply, // Type of the reply argument.
void (TSvc::*Method)(const TReq&, ::perfetto::ipc::Deferred<TReply>)>
void _IPC_Invoker(::perfetto::ipc::Service* s,
const ::perfetto::ipc::ProtoMessage& req,
::perfetto::ipc::DeferredBase reply) {
(*static_cast<TSvc*>(s).*Method)(
static_cast<const TReq&>(req),
::perfetto::ipc::Deferred<TReply>(::std::move(reply)));
}
#endif // INCLUDE_PERFETTO_EXT_IPC_CODEGEN_HELPERS_H_

View file

@ -0,0 +1,136 @@
/*
* Copyright (C) 2017 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 INCLUDE_PERFETTO_EXT_IPC_DEFERRED_H_
#define INCLUDE_PERFETTO_EXT_IPC_DEFERRED_H_
#include <functional>
#include <memory>
#include <utility>
#include "perfetto/ext/ipc/async_result.h"
#include "perfetto/ext/ipc/basic_types.h"
namespace perfetto {
namespace ipc {
// This class is a wrapper for a callback handling async results.
// The problem this is solving is the following: For each result argument of the
// methods generated from the .proto file:
// - The client wants to see something on which it can Bind() a callback, which
// is invoked asynchronously once reply is received from the host.
// - The host wants to expose something to user code that implements the IPC
// methods to allow them to provide an asynchronous reply back to the client.
// Eventually even more than once, for the case streaming replies.
//
// In both cases we want to make sure that callbacks don't get lost along the
// way. To address this, this class will automatically reject the callbacks
// if they are not resolved at destructor time (or the object is std::move()'d).
//
// The client is supposed to use this class as follows:
// class GreeterProxy {
// void SayHello(const HelloRequest&, Deferred<HelloReply> reply)
// }
// ...
// Deferred<HelloReply> reply;
// reply.Bind([] (AsyncResult<HelloReply> reply) {
// std::cout << reply.success() ? reply->message : "failure";
// });
// host_proxy_instance.SayHello(req, std::move(reply));
//
// The host instead is supposed to use this as follows:
// class GreeterImpl : public Greeter {
// void SayHello(const HelloRequest& req, Deferred<HelloReply> reply) {
// AsyncResult<HelloReply> reply = AsyncResult<HelloReply>::Create();
// reply->set_greeting("Hello " + req.name)
// reply.Resolve(std::move(reply));
// }
// }
// Or for more complex cases, the deferred object can be std::move()'d outside
// and the reply can continue asynchronously later.
template <typename T>
class Deferred;
class DeferredBase {
public:
explicit DeferredBase(
std::function<void(AsyncResult<ProtoMessage>)> callback = nullptr);
~DeferredBase();
DeferredBase(DeferredBase&&) noexcept;
DeferredBase& operator=(DeferredBase&&);
void Bind(std::function<void(AsyncResult<ProtoMessage>)> callback);
bool IsBound() const;
void Resolve(AsyncResult<ProtoMessage>);
void Reject();
protected:
template <typename T>
friend class Deferred;
void Move(DeferredBase&);
std::function<void(AsyncResult<ProtoMessage>)> callback_;
};
template <typename T> // T : ProtoMessage subclass
class Deferred : public DeferredBase {
public:
explicit Deferred(std::function<void(AsyncResult<T>)> callback = nullptr) {
Bind(std::move(callback));
}
// This move constructor (and the similar one in DeferredBase) is meant to be
// called only by the autogenerated code. The caller has to guarantee that the
// moved-from and moved-to types match. The behavior is otherwise undefined.
explicit Deferred(DeferredBase&& other) {
callback_ = std::move(other.callback_);
other.callback_ = nullptr;
}
void Bind(std::function<void(AsyncResult<T>)> callback) {
if (!callback)
return;
// Here we need a callback adapter to downcast the callback to a generic
// callback that takes an AsyncResult<ProtoMessage>, so that it can be
// stored in the base class |callback_|.
auto callback_adapter = [callback](
AsyncResult<ProtoMessage> async_result_base) {
// Upcast the async_result from <ProtoMessage> -> <T : ProtoMessage>.
static_assert(std::is_base_of<ProtoMessage, T>::value, "T:ProtoMessage");
AsyncResult<T> async_result(
std::unique_ptr<T>(static_cast<T*>(async_result_base.release_msg())),
async_result_base.has_more(), async_result_base.fd());
callback(std::move(async_result));
};
DeferredBase::Bind(callback_adapter);
}
// If no more messages are expected, |callback_| is released.
void Resolve(AsyncResult<T> async_result) {
// Convert the |async_result| to the generic base one (T -> ProtoMessage).
AsyncResult<ProtoMessage> async_result_base(
std::unique_ptr<ProtoMessage>(async_result.release_msg()),
async_result.has_more(), async_result.fd());
DeferredBase::Resolve(std::move(async_result_base));
}
};
} // namespace ipc
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_IPC_DEFERRED_H_

View file

@ -0,0 +1,83 @@
/*
* Copyright (C) 2017 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 INCLUDE_PERFETTO_EXT_IPC_HOST_H_
#define INCLUDE_PERFETTO_EXT_IPC_HOST_H_
#include <memory>
#include "perfetto/ext/base/scoped_file.h"
#include "perfetto/ext/base/unix_socket.h"
#include "perfetto/ext/ipc/basic_types.h"
namespace perfetto {
namespace base {
class TaskRunner;
} // namespace base
namespace ipc {
class Service;
// The host-side of the IPC layer. This class acts as a registry and request
// dispatcher. It listen on the UnixSocket |socket_name| for incoming requests
// (coming Client instances) and dispatches their requests to the various
// Services exposed.
class Host {
public:
// Creates an instance and starts listening on the given |socket_name|.
// Returns nullptr if listening on the socket fails.
static std::unique_ptr<Host> CreateInstance(const char* socket_name,
base::TaskRunner*);
// Like the above but takes a file descriptor to a pre-bound unix socket.
// Returns nullptr if listening on the socket fails.
static std::unique_ptr<Host> CreateInstance(base::ScopedSocketHandle,
base::TaskRunner*);
// Creates a Host which is not backed by a POSIX listening socket.
// Instead, it accepts sockets passed in via AdoptConnectedSocket_Fuchsia().
// See go/fuchsetto for more details.
static std::unique_ptr<Host> CreateInstance_Fuchsia(base::TaskRunner*);
virtual ~Host();
// Registers a new service and makes it available to remote IPC peers.
// All the exposed Service instances will be destroyed when destroying the
// Host instance if ExposeService succeeds and returns true, or immediately
// after the call in case of failure.
// Returns true if the register has been successfully registered, false in
// case of errors (e.g., another service with the same name is already
// registered).
virtual bool ExposeService(std::unique_ptr<Service>) = 0;
// Accepts a pre-connected socket handle and a callback used to send a
// shared memory FD to the remote client.
// The callback returns false if the FD could not be sent.
// Should only be used in conjunction with CreateInstance_Fuchsia().
virtual void AdoptConnectedSocket_Fuchsia(
base::ScopedSocketHandle,
std::function<bool(int)> send_fd_cb) = 0;
// Overrides the default send timeout for the per-connection sockets.
virtual void SetSocketSendTimeoutMs(uint32_t timeout_ms) = 0;
};
} // namespace ipc
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_IPC_HOST_H_

View file

@ -0,0 +1,80 @@
/*
* Copyright (C) 2017 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 INCLUDE_PERFETTO_EXT_IPC_SERVICE_H_
#define INCLUDE_PERFETTO_EXT_IPC_SERVICE_H_
#include "perfetto/base/logging.h"
#include "perfetto/ext/base/scoped_file.h"
#include "perfetto/ext/ipc/client_info.h"
namespace perfetto {
namespace ipc {
class ServiceDescriptor;
// The base class for all the autogenerated host-side service interfaces.
class Service {
public:
virtual ~Service();
// Overridden by the auto-generated class. Provides the list of methods and
// the protobuf (de)serialization functions for their arguments.
virtual const ServiceDescriptor& GetDescriptor() = 0;
// Invoked when a remote client disconnects. Use client_info() to obtain
// details about the client that disconnected.
virtual void OnClientDisconnected() {}
// Returns the ClientInfo for the current IPC request. Returns an invalid
// ClientInfo if called outside the scope of an IPC method.
const ClientInfo& client_info() {
PERFETTO_DCHECK(client_info_.is_valid());
return client_info_;
}
base::ScopedFile TakeReceivedFD() {
if (received_fd_)
return std::move(*received_fd_);
return base::ScopedFile();
}
bool use_shmem_emulation() { return use_shmem_emulation_; }
private:
friend class HostImpl;
ClientInfo client_info_;
// This is a pointer because the received fd needs to remain owned by the
// ClientConnection, as we will provide it to all method invocations
// for that client until one of them calls Service::TakeReceivedFD.
//
// Different clients might have sent different FDs so this cannot be owned
// here.
//
// Note that this means that there can always only be one outstanding
// invocation per client that supplies an FD and the client needs to
// wait for this one to return before calling another one.
base::ScopedFile* received_fd_;
// Whether the socket needs to emulate shared memory buffer. Set by HostImpl
// when the service is exposed.
bool use_shmem_emulation_ = false;
};
} // namespace ipc
} // namespace perfetto
#endif // INCLUDE_PERFETTO_EXT_IPC_SERVICE_H_

Some files were not shown because too many files have changed in this diff Show more