forked from lix-project/lix
tracing: wip i am going to regret not splitting this right now #1
|
@ -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',
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
118
src/libexpr/fast-function-tracer.cc
Normal file
118
src/libexpr/fast-function-tracer.cc
Normal 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,
|
||||
};
|
||||
}
|
||||
|
||||
}
|
42
src/libexpr/fast-function-tracer.hh
Normal file
42
src/libexpr/fast-function-tracer.hh
Normal 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();
|
||||
};
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -10,6 +10,10 @@ class PosIdx
|
|||
friend struct LazyPosAcessors;
|
||||
friend class PosTable;
|
||||
|
||||
// FastFunctionTracer internals
|
||||
friend class EvalStateRetriever;
|
||||
friend class FastFunctionTracer;
|
||||
|
||||
private:
|
||||
uint32_t id;
|
||||
|
||||
|
|
|
@ -52,6 +52,10 @@ class Symbol
|
|||
{
|
||||
friend class SymbolTable;
|
||||
|
||||
// FastFunctionTracer internals
|
||||
friend class EvalStateRetriever;
|
||||
friend class FastFunctionTracer;
|
||||
|
||||
private:
|
||||
uint32_t id;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
Logger * makeProgressBar();
|
||||
std::shared_ptr<Logger> makeProgressBar();
|
||||
|
||||
void startProgressBar();
|
||||
|
||||
|
|
|
@ -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
45
src/libtracing/main.cc
Normal 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;
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
// Intentionally empty (crbug.com/998165)
|
123429
src/libtracing/meow/protos/perfetto/trace/perfetto_trace.pbzero.h
Normal file
123429
src/libtracing/meow/protos/perfetto/trace/perfetto_trace.pbzero.h
Normal file
File diff suppressed because it is too large
Load diff
47
src/libtracing/meson.build
Normal file
47
src/libtracing/meson.build
Normal 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,
|
||||
)
|
33
src/libtracing/perfetto/base/BUILD.gn
Normal file
33
src/libtracing/perfetto/base/BUILD.gn
Normal 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" ]
|
||||
}
|
160
src/libtracing/perfetto/base/build_config.h
Normal file
160
src/libtracing/perfetto/base/build_config.h
Normal 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_
|
|
@ -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_
|
|
@ -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_
|
157
src/libtracing/perfetto/base/compiler.h
Normal file
157
src/libtracing/perfetto/base/compiler.h
Normal 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_
|
41
src/libtracing/perfetto/base/export.h
Normal file
41
src/libtracing/perfetto/base/export.h
Normal 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_
|
100
src/libtracing/perfetto/base/flat_set.h
Normal file
100
src/libtracing/perfetto/base/flat_set.h
Normal 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_
|
248
src/libtracing/perfetto/base/logging.h
Normal file
248
src/libtracing/perfetto/base/logging.h
Normal 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_
|
71
src/libtracing/perfetto/base/platform_handle.h
Normal file
71
src/libtracing/perfetto/base/platform_handle.h
Normal 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_
|
59
src/libtracing/perfetto/base/proc_utils.h
Normal file
59
src/libtracing/perfetto/base/proc_utils.h
Normal 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_
|
117
src/libtracing/perfetto/base/status.h
Normal file
117
src/libtracing/perfetto/base/status.h
Normal 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_
|
78
src/libtracing/perfetto/base/task_runner.h
Normal file
78
src/libtracing/perfetto/base/task_runner.h
Normal 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_
|
75
src/libtracing/perfetto/base/template_util.h
Normal file
75
src/libtracing/perfetto/base/template_util.h
Normal 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_
|
85
src/libtracing/perfetto/base/thread_utils.h
Normal file
85
src/libtracing/perfetto/base/thread_utils.h
Normal 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_
|
294
src/libtracing/perfetto/base/time.h
Normal file
294
src/libtracing/perfetto/base/time.h
Normal 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_
|
73
src/libtracing/perfetto/ext/base/BUILD.gn
Normal file
73
src/libtracing/perfetto/ext/base/BUILD.gn
Normal 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" ]
|
||||
}
|
39
src/libtracing/perfetto/ext/base/android_utils.h
Normal file
39
src/libtracing/perfetto/ext/base/android_utils.h
Normal 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_
|
68
src/libtracing/perfetto/ext/base/base64.h
Normal file
68
src/libtracing/perfetto/ext/base/base64.h
Normal 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_
|
337
src/libtracing/perfetto/ext/base/circular_queue.h
Normal file
337
src/libtracing/perfetto/ext/base/circular_queue.h
Normal 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_
|
59
src/libtracing/perfetto/ext/base/container_annotations.h
Normal file
59
src/libtracing/perfetto/ext/base/container_annotations.h
Normal 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_
|
162
src/libtracing/perfetto/ext/base/crash_keys.h
Normal file
162
src/libtracing/perfetto/ext/base/crash_keys.h
Normal 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_
|
32
src/libtracing/perfetto/ext/base/ctrl_c_handler.h
Normal file
32
src/libtracing/perfetto/ext/base/ctrl_c_handler.h
Normal 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_
|
58
src/libtracing/perfetto/ext/base/endian.h
Normal file
58
src/libtracing/perfetto/ext/base/endian.h
Normal 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_
|
64
src/libtracing/perfetto/ext/base/event_fd.h
Normal file
64
src/libtracing/perfetto/ext/base/event_fd.h
Normal 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_
|
114
src/libtracing/perfetto/ext/base/file_utils.h
Normal file
114
src/libtracing/perfetto/ext/base/file_utils.h
Normal 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_
|
395
src/libtracing/perfetto/ext/base/flat_hash_map.h
Normal file
395
src/libtracing/perfetto/ext/base/flat_hash_map.h
Normal 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_
|
51
src/libtracing/perfetto/ext/base/getopt.h
Normal file
51
src/libtracing/perfetto/ext/base/getopt.h
Normal 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_
|
71
src/libtracing/perfetto/ext/base/getopt_compat.h
Normal file
71
src/libtracing/perfetto/ext/base/getopt_compat.h
Normal 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_
|
137
src/libtracing/perfetto/ext/base/hash.h
Normal file
137
src/libtracing/perfetto/ext/base/hash.h
Normal 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_
|
21
src/libtracing/perfetto/ext/base/http/BUILD.gn
Normal file
21
src/libtracing/perfetto/ext/base/http/BUILD.gn
Normal 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" ]
|
||||
}
|
189
src/libtracing/perfetto/ext/base/http/http_server.h
Normal file
189
src/libtracing/perfetto/ext/base/http/http_server.h
Normal 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_
|
39
src/libtracing/perfetto/ext/base/http/sha1.h
Normal file
39
src/libtracing/perfetto/ext/base/http/sha1.h
Normal 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_
|
318
src/libtracing/perfetto/ext/base/metatrace.h
Normal file
318
src/libtracing/perfetto/ext/base/metatrace.h
Normal 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_
|
117
src/libtracing/perfetto/ext/base/metatrace_events.h
Normal file
117
src/libtracing/perfetto/ext/base/metatrace_events.h
Normal 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_
|
78
src/libtracing/perfetto/ext/base/no_destructor.h
Normal file
78
src/libtracing/perfetto/ext/base/no_destructor.h
Normal 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_
|
105
src/libtracing/perfetto/ext/base/paged_memory.h
Normal file
105
src/libtracing/perfetto/ext/base/paged_memory.h
Normal 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_
|
90
src/libtracing/perfetto/ext/base/periodic_task.h
Normal file
90
src/libtracing/perfetto/ext/base/periodic_task.h
Normal 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_
|
50
src/libtracing/perfetto/ext/base/pipe.h
Normal file
50
src/libtracing/perfetto/ext/base/pipe.h
Normal 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_
|
40
src/libtracing/perfetto/ext/base/platform.h
Normal file
40
src/libtracing/perfetto/ext/base/platform.h
Normal 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_
|
122
src/libtracing/perfetto/ext/base/scoped_file.h
Normal file
122
src/libtracing/perfetto/ext/base/scoped_file.h
Normal 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_
|
92
src/libtracing/perfetto/ext/base/scoped_mmap.h
Normal file
92
src/libtracing/perfetto/ext/base/scoped_mmap.h
Normal 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_
|
62
src/libtracing/perfetto/ext/base/small_set.h
Normal file
62
src/libtracing/perfetto/ext/base/small_set.h
Normal 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_
|
197
src/libtracing/perfetto/ext/base/small_vector.h
Normal file
197
src/libtracing/perfetto/ext/base/small_vector.h
Normal 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_
|
83
src/libtracing/perfetto/ext/base/status_or.h
Normal file
83
src/libtracing/perfetto/ext/base/status_or.h
Normal 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_
|
90
src/libtracing/perfetto/ext/base/string_splitter.h
Normal file
90
src/libtracing/perfetto/ext/base/string_splitter.h
Normal 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_
|
233
src/libtracing/perfetto/ext/base/string_utils.h
Normal file
233
src/libtracing/perfetto/ext/base/string_utils.h
Normal 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_
|
201
src/libtracing/perfetto/ext/base/string_view.h
Normal file
201
src/libtracing/perfetto/ext/base/string_view.h
Normal 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_
|
183
src/libtracing/perfetto/ext/base/string_writer.h
Normal file
183
src/libtracing/perfetto/ext/base/string_writer.h
Normal 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_
|
281
src/libtracing/perfetto/ext/base/subprocess.h
Normal file
281
src/libtracing/perfetto/ext/base/subprocess.h
Normal 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_
|
63
src/libtracing/perfetto/ext/base/sys_types.h
Normal file
63
src/libtracing/perfetto/ext/base/sys_types.h
Normal 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_
|
80
src/libtracing/perfetto/ext/base/temp_file.h
Normal file
80
src/libtracing/perfetto/ext/base/temp_file.h
Normal 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_
|
38
src/libtracing/perfetto/ext/base/thread_annotations.h
Normal file
38
src/libtracing/perfetto/ext/base/thread_annotations.h
Normal 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_
|
68
src/libtracing/perfetto/ext/base/thread_checker.h
Normal file
68
src/libtracing/perfetto/ext/base/thread_checker.h
Normal 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_
|
85
src/libtracing/perfetto/ext/base/thread_task_runner.h
Normal file
85
src/libtracing/perfetto/ext/base/thread_task_runner.h
Normal 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_
|
90
src/libtracing/perfetto/ext/base/thread_utils.h
Normal file
90
src/libtracing/perfetto/ext/base/thread_utils.h
Normal 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_
|
33
src/libtracing/perfetto/ext/base/threading/BUILD.gn
Normal file
33
src/libtracing/perfetto/ext/base/threading/BUILD.gn
Normal 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",
|
||||
]
|
||||
}
|
180
src/libtracing/perfetto/ext/base/threading/channel.h
Normal file
180
src/libtracing/perfetto/ext/base/threading/channel.h
Normal 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_
|
149
src/libtracing/perfetto/ext/base/threading/future.h
Normal file
149
src/libtracing/perfetto/ext/base/threading/future.h
Normal 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_
|
|
@ -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_
|
245
src/libtracing/perfetto/ext/base/threading/poll.h
Normal file
245
src/libtracing/perfetto/ext/base/threading/poll.h
Normal 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_
|
149
src/libtracing/perfetto/ext/base/threading/spawn.h
Normal file
149
src/libtracing/perfetto/ext/base/threading/spawn.h
Normal 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_
|
190
src/libtracing/perfetto/ext/base/threading/stream.h
Normal file
190
src/libtracing/perfetto/ext/base/threading/stream.h
Normal 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_
|
315
src/libtracing/perfetto/ext/base/threading/stream_combinators.h
Normal file
315
src/libtracing/perfetto/ext/base/threading/stream_combinators.h
Normal 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_
|
80
src/libtracing/perfetto/ext/base/threading/thread_pool.h
Normal file
80
src/libtracing/perfetto/ext/base/threading/thread_pool.h
Normal 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_
|
210
src/libtracing/perfetto/ext/base/threading/util.h
Normal file
210
src/libtracing/perfetto/ext/base/threading/util.h
Normal 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_
|
464
src/libtracing/perfetto/ext/base/unix_socket.h
Normal file
464
src/libtracing/perfetto/ext/base/unix_socket.h
Normal 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_
|
133
src/libtracing/perfetto/ext/base/unix_task_runner.h
Normal file
133
src/libtracing/perfetto/ext/base/unix_task_runner.h
Normal 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_
|
196
src/libtracing/perfetto/ext/base/utils.h
Normal file
196
src/libtracing/perfetto/ext/base/utils.h
Normal 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_
|
75
src/libtracing/perfetto/ext/base/uuid.h
Normal file
75
src/libtracing/perfetto/ext/base/uuid.h
Normal 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_
|
47
src/libtracing/perfetto/ext/base/version.h
Normal file
47
src/libtracing/perfetto/ext/base/version.h
Normal 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_
|
50
src/libtracing/perfetto/ext/base/waitable_event.h
Normal file
50
src/libtracing/perfetto/ext/base/waitable_event.h
Normal 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_
|
79
src/libtracing/perfetto/ext/base/watchdog.h
Normal file
79
src/libtracing/perfetto/ext/base/watchdog.h
Normal 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_
|
51
src/libtracing/perfetto/ext/base/watchdog_noop.h
Normal file
51
src/libtracing/perfetto/ext/base/watchdog_noop.h
Normal 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_
|
202
src/libtracing/perfetto/ext/base/watchdog_posix.h
Normal file
202
src/libtracing/perfetto/ext/base/watchdog_posix.h
Normal 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_
|
120
src/libtracing/perfetto/ext/base/weak_ptr.h
Normal file
120
src/libtracing/perfetto/ext/base/weak_ptr.h
Normal 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_
|
32
src/libtracing/perfetto/ext/ipc/BUILD.gn
Normal file
32
src/libtracing/perfetto/ext/ipc/BUILD.gn
Normal 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",
|
||||
]
|
||||
}
|
74
src/libtracing/perfetto/ext/ipc/async_result.h
Normal file
74
src/libtracing/perfetto/ext/ipc/async_result.h
Normal 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_
|
55
src/libtracing/perfetto/ext/ipc/basic_types.h
Normal file
55
src/libtracing/perfetto/ext/ipc/basic_types.h
Normal 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_
|
89
src/libtracing/perfetto/ext/ipc/client.h
Normal file
89
src/libtracing/perfetto/ext/ipc/client.h
Normal 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_
|
77
src/libtracing/perfetto/ext/ipc/client_info.h
Normal file
77
src/libtracing/perfetto/ext/ipc/client_info.h
Normal 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_
|
54
src/libtracing/perfetto/ext/ipc/codegen_helpers.h
Normal file
54
src/libtracing/perfetto/ext/ipc/codegen_helpers.h
Normal 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_
|
136
src/libtracing/perfetto/ext/ipc/deferred.h
Normal file
136
src/libtracing/perfetto/ext/ipc/deferred.h
Normal 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_
|
83
src/libtracing/perfetto/ext/ipc/host.h
Normal file
83
src/libtracing/perfetto/ext/ipc/host.h
Normal 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_
|
80
src/libtracing/perfetto/ext/ipc/service.h
Normal file
80
src/libtracing/perfetto/ext/ipc/service.h
Normal 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
Loading…
Reference in a new issue