forked from lix-project/lix
tracing: wip i am going to regret not splitting this right now #1
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_
|
72
src/libtracing/perfetto/ext/ipc/service_descriptor.h
Normal file
72
src/libtracing/perfetto/ext/ipc/service_descriptor.h
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* 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_DESCRIPTOR_H_
|
||||||
|
#define INCLUDE_PERFETTO_EXT_IPC_SERVICE_DESCRIPTOR_H_
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "perfetto/ext/ipc/basic_types.h"
|
||||||
|
#include "perfetto/ext/ipc/deferred.h"
|
||||||
|
|
||||||
|
namespace perfetto {
|
||||||
|
namespace ipc {
|
||||||
|
|
||||||
|
class Service;
|
||||||
|
|
||||||
|
// This is a pure data structure which holds factory methods and strings for the
|
||||||
|
// services and their methods that get generated in the .h/.cc files.
|
||||||
|
// Each autogenerated class has a GetDescriptor() method that returns one
|
||||||
|
// instance of these and allows both client and hosts to map service and method
|
||||||
|
// names to IDs and provide function pointers to the protobuf decoder fuctions.
|
||||||
|
class ServiceDescriptor {
|
||||||
|
public:
|
||||||
|
struct Method {
|
||||||
|
const char* name;
|
||||||
|
|
||||||
|
// DecoderFunc is pointer to a function that takes a string in input
|
||||||
|
// containing protobuf encoded data and returns a decoded protobuf message.
|
||||||
|
using DecoderFunc = std::unique_ptr<ProtoMessage> (*)(const std::string&);
|
||||||
|
|
||||||
|
// Function pointer to decode the request argument of the method.
|
||||||
|
DecoderFunc request_proto_decoder;
|
||||||
|
|
||||||
|
// Function pointer to decoded the reply argument of the method.
|
||||||
|
DecoderFunc reply_proto_decoder;
|
||||||
|
|
||||||
|
// Function pointer that dispatches the generic request to the corresponding
|
||||||
|
// method implementation.
|
||||||
|
using InvokerFunc = void (*)(Service*,
|
||||||
|
const ProtoMessage& /* request_args */,
|
||||||
|
DeferredBase /* deferred_reply */);
|
||||||
|
InvokerFunc invoker;
|
||||||
|
};
|
||||||
|
|
||||||
|
const char* service_name = nullptr;
|
||||||
|
|
||||||
|
// Note that methods order is not stable. Client and Host might have different
|
||||||
|
// method indexes, depending on their versions. The Client can't just rely
|
||||||
|
// on the indexes and has to keep a [string -> remote index] translation map.
|
||||||
|
std::vector<Method> methods;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ipc
|
||||||
|
} // namespace perfetto
|
||||||
|
|
||||||
|
#endif // INCLUDE_PERFETTO_EXT_IPC_SERVICE_DESCRIPTOR_H_
|
102
src/libtracing/perfetto/ext/ipc/service_proxy.h
Normal file
102
src/libtracing/perfetto/ext/ipc/service_proxy.h
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
/*
|
||||||
|
* 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_PROXY_H_
|
||||||
|
#define INCLUDE_PERFETTO_EXT_IPC_SERVICE_PROXY_H_
|
||||||
|
|
||||||
|
#include "perfetto/ext/ipc/basic_types.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "perfetto/base/export.h"
|
||||||
|
#include "perfetto/ext/base/weak_ptr.h"
|
||||||
|
#include "perfetto/ext/ipc/deferred.h"
|
||||||
|
|
||||||
|
namespace perfetto {
|
||||||
|
namespace ipc {
|
||||||
|
|
||||||
|
class Client;
|
||||||
|
class ServiceDescriptor;
|
||||||
|
|
||||||
|
// The base class for the client-side autogenerated stubs that forward method
|
||||||
|
// invocations to the host. All the methods of this class are meant to be called
|
||||||
|
// only by the autogenerated code.
|
||||||
|
class PERFETTO_EXPORT_COMPONENT ServiceProxy {
|
||||||
|
public:
|
||||||
|
class EventListener {
|
||||||
|
public:
|
||||||
|
virtual ~EventListener();
|
||||||
|
|
||||||
|
// Called once after Client::BindService() if the ServiceProxy has been
|
||||||
|
// successfully bound to the host. It is possible to start sending IPC
|
||||||
|
// requests soon after this.
|
||||||
|
virtual void OnConnect() {}
|
||||||
|
|
||||||
|
// Called if the connection fails to be established or drops after having
|
||||||
|
// been established.
|
||||||
|
virtual void OnDisconnect() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Guarantees that no callback will happen after this object has been
|
||||||
|
// destroyed. The caller has to guarantee that the |event_listener| stays
|
||||||
|
// alive at least as long as the ServiceProxy instance.
|
||||||
|
explicit ServiceProxy(EventListener*);
|
||||||
|
virtual ~ServiceProxy();
|
||||||
|
|
||||||
|
void InitializeBinding(base::WeakPtr<Client>,
|
||||||
|
ServiceID,
|
||||||
|
std::map<std::string, MethodID>);
|
||||||
|
|
||||||
|
// Called by the IPC methods in the autogenerated classes.
|
||||||
|
void BeginInvoke(const std::string& method_name,
|
||||||
|
const ProtoMessage& request,
|
||||||
|
DeferredBase reply,
|
||||||
|
int fd = -1);
|
||||||
|
|
||||||
|
// Called by ClientImpl.
|
||||||
|
// |reply_args| == nullptr means request failure.
|
||||||
|
void EndInvoke(RequestID,
|
||||||
|
std::unique_ptr<ProtoMessage> reply_arg,
|
||||||
|
bool has_more);
|
||||||
|
|
||||||
|
// Called by ClientImpl.
|
||||||
|
void OnConnect(bool success);
|
||||||
|
void OnDisconnect();
|
||||||
|
bool connected() const { return service_id_ != 0; }
|
||||||
|
|
||||||
|
base::WeakPtr<ServiceProxy> GetWeakPtr() const;
|
||||||
|
|
||||||
|
// Implemented by the autogenerated class.
|
||||||
|
virtual const ServiceDescriptor& GetDescriptor() = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
base::WeakPtr<Client> client_;
|
||||||
|
ServiceID service_id_ = 0;
|
||||||
|
std::map<std::string, MethodID> remote_method_ids_;
|
||||||
|
std::map<RequestID, DeferredBase> pending_callbacks_;
|
||||||
|
EventListener* const event_listener_;
|
||||||
|
base::WeakPtrFactory<ServiceProxy> weak_ptr_factory_; // Keep last.
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ipc
|
||||||
|
} // namespace perfetto
|
||||||
|
|
||||||
|
#endif // INCLUDE_PERFETTO_EXT_IPC_SERVICE_PROXY_H_
|
21
src/libtracing/perfetto/ext/protozero/BUILD.gn
Normal file
21
src/libtracing/perfetto/ext/protozero/BUILD.gn
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import("../../../../gn/perfetto.gni")
|
||||||
|
|
||||||
|
source_set("protozero") {
|
||||||
|
public_deps = [ "../base" ]
|
||||||
|
deps = [ "../../../../gn:default_deps" ]
|
||||||
|
sources = [ "proto_ring_buffer.h" ]
|
||||||
|
}
|
153
src/libtracing/perfetto/ext/protozero/proto_ring_buffer.h
Normal file
153
src/libtracing/perfetto/ext/protozero/proto_ring_buffer.h
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
/*
|
||||||
|
* 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_PROTOZERO_PROTO_RING_BUFFER_H_
|
||||||
|
#define INCLUDE_PERFETTO_EXT_PROTOZERO_PROTO_RING_BUFFER_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "perfetto/ext/base/paged_memory.h"
|
||||||
|
|
||||||
|
namespace protozero {
|
||||||
|
|
||||||
|
// This class buffers and tokenizes proto messages.
|
||||||
|
//
|
||||||
|
// From a logical level, it works with a sequence of protos like this.
|
||||||
|
// [ header 1 ] [ payload 1 ]
|
||||||
|
// [ header 2 ] [ payload 2 ]
|
||||||
|
// [ header 3 ] [ payload 3 ]
|
||||||
|
// Where [ header ] is a variable-length sequence of:
|
||||||
|
// [ Field ID = 1, type = length-delimited] [ length (varint) ].
|
||||||
|
//
|
||||||
|
// The input to this class is byte-oriented, not message-oriented (like a TCP
|
||||||
|
// stream or a pipe). The caller is not required to respect the boundaries of
|
||||||
|
// each message; only guarantee that data is not lost or duplicated. The
|
||||||
|
// following sequence of inbound events is possible:
|
||||||
|
// 1. [ hdr 1 (incomplete) ... ]
|
||||||
|
// 2. [ ... hdr 1 ] [ payload 1 ] [ hdr 2 ] [ payoad 2 ] [ hdr 3 ] [ pay... ]
|
||||||
|
// 3. [ ...load 3 ]
|
||||||
|
//
|
||||||
|
// This class maintains inbound requests in a ring buffer.
|
||||||
|
// The expected usage is:
|
||||||
|
// ring_buf.Append(data, len);
|
||||||
|
// for (;;) {
|
||||||
|
// auto msg = ring_buf.ReadMessage();
|
||||||
|
// if (!msg.valid())
|
||||||
|
// break;
|
||||||
|
// Decode(msg);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// After each call to Append, the caller is expected to call ReadMessage() until
|
||||||
|
// it returns an invalid message (signalling no more messages could be decoded).
|
||||||
|
// Note that a single Append can "unblock" > 1 messages, which is why the caller
|
||||||
|
// needs to keep calling ReadMessage in a loop.
|
||||||
|
//
|
||||||
|
// Internal architecture
|
||||||
|
// ---------------------
|
||||||
|
// Internally this is similar to a ring-buffer, with the caveat that it never
|
||||||
|
// wraps, it only expands. Expansions are rare. The deal is that in most cases
|
||||||
|
// the read cursor follows very closely the write cursor. For instance, if the
|
||||||
|
// underlying transport behaves as a dgram socket, after each Append, the read
|
||||||
|
// cursor will chase completely the write cursor. Even if the underlying stream
|
||||||
|
// is not always atomic, the expectation is that the read cursor will eventually
|
||||||
|
// reach the write one within few messages.
|
||||||
|
// A visual example, imagine we have four messages: 2it 4will 2be 4fine
|
||||||
|
// Visually:
|
||||||
|
//
|
||||||
|
// Append("2it4wi"): A message and a bit:
|
||||||
|
// [ 2it 4wi ]
|
||||||
|
// ^R ^W
|
||||||
|
//
|
||||||
|
// After the ReadMessage(), the 1st message will be read, but not the 2nd.
|
||||||
|
// [ 2it 4wi ]
|
||||||
|
// ^R ^W
|
||||||
|
//
|
||||||
|
// Append("ll2be4f")
|
||||||
|
// [ 2it 4will 2be 4f ]
|
||||||
|
// ^R ^W
|
||||||
|
//
|
||||||
|
// After the ReadMessage() loop:
|
||||||
|
// [ 2it 4will 2be 4f ]
|
||||||
|
// ^R ^W
|
||||||
|
// Append("ine")
|
||||||
|
// [ 2it 4will 2be 4fine ]
|
||||||
|
// ^R ^W
|
||||||
|
//
|
||||||
|
// In the next ReadMessage() the R cursor will chase the W cursor. When this
|
||||||
|
// happens (very frequent) we can just reset both cursors to 0 and restart.
|
||||||
|
// If we are unlucky and get to the end of the buffer, two things happen:
|
||||||
|
// 1. We try first to recompact the buffer, moving everything left by R.
|
||||||
|
// 2. If still there isn't enough space, we expand the buffer.
|
||||||
|
// Given that each message is expected to be at most kMaxMsgSize (64 MB), the
|
||||||
|
// expansion is bound at 2 * kMaxMsgSize.
|
||||||
|
|
||||||
|
class RingBufferMessageReader {
|
||||||
|
public:
|
||||||
|
static constexpr size_t kMaxMsgSize = 64 * 1024 * 1024;
|
||||||
|
struct Message {
|
||||||
|
const uint8_t* start = nullptr;
|
||||||
|
uint32_t len = 0;
|
||||||
|
uint32_t field_id = 0;
|
||||||
|
bool fatal_framing_error = false;
|
||||||
|
const uint8_t* end() const { return start + len; }
|
||||||
|
inline bool valid() const { return !!start; }
|
||||||
|
};
|
||||||
|
|
||||||
|
RingBufferMessageReader();
|
||||||
|
virtual ~RingBufferMessageReader();
|
||||||
|
RingBufferMessageReader(const RingBufferMessageReader&) = delete;
|
||||||
|
RingBufferMessageReader& operator=(const RingBufferMessageReader&) = delete;
|
||||||
|
|
||||||
|
// Appends data into the ring buffer, recompacting or resizing it if needed.
|
||||||
|
// Will invaildate the pointers previously handed out.
|
||||||
|
void Append(const void* data, size_t len);
|
||||||
|
|
||||||
|
// If a message can be read, it returns the boundaries of the message
|
||||||
|
// (without including the preamble) and advances the read cursor.
|
||||||
|
// If no message is available, returns a null range.
|
||||||
|
// The returned pointer is only valid until the next call to Append(), as
|
||||||
|
// that can recompact or resize the underlying buffer.
|
||||||
|
Message ReadMessage();
|
||||||
|
|
||||||
|
// Exposed for testing.
|
||||||
|
size_t capacity() const { return buf_.size(); }
|
||||||
|
size_t avail() const { return buf_.size() - (wr_ - rd_); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Subclasses must implement the header parsing.
|
||||||
|
virtual Message TryReadMessage(const uint8_t* start, const uint8_t* end) = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
perfetto::base::PagedMemory buf_;
|
||||||
|
Message fastpath_{};
|
||||||
|
bool failed_ = false; // Set in case of an unrecoverable framing faiulre.
|
||||||
|
size_t rd_ = 0; // Offset of the read cursor in |buf_|.
|
||||||
|
size_t wr_ = 0; // Offset of the write cursor in |buf_|.
|
||||||
|
};
|
||||||
|
|
||||||
|
class ProtoRingBuffer final : public RingBufferMessageReader {
|
||||||
|
public:
|
||||||
|
ProtoRingBuffer();
|
||||||
|
~ProtoRingBuffer() override final;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Message TryReadMessage(const uint8_t* start,
|
||||||
|
const uint8_t* end) override final;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace protozero
|
||||||
|
|
||||||
|
#endif // INCLUDE_PERFETTO_EXT_PROTOZERO_PROTO_RING_BUFFER_H_
|
27
src/libtracing/perfetto/ext/trace_processor/BUILD.gn
Normal file
27
src/libtracing/perfetto/ext/trace_processor/BUILD.gn
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
# 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")
|
||||||
|
|
||||||
|
# Exposed for Chromium only. May be removed in the future.
|
||||||
|
source_set("export_json") {
|
||||||
|
sources = []
|
||||||
|
if (enable_perfetto_trace_processor_json) {
|
||||||
|
sources += [ "export_json.h" ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
source_set("demangle") {
|
||||||
|
sources = [ "demangle.h" ]
|
||||||
|
}
|
34
src/libtracing/perfetto/ext/trace_processor/demangle.h
Normal file
34
src/libtracing/perfetto/ext/trace_processor/demangle.h
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* 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_TRACE_PROCESSOR_DEMANGLE_H_
|
||||||
|
#define INCLUDE_PERFETTO_EXT_TRACE_PROCESSOR_DEMANGLE_H_
|
||||||
|
|
||||||
|
#include "perfetto/ext/base/utils.h"
|
||||||
|
|
||||||
|
namespace perfetto {
|
||||||
|
namespace trace_processor {
|
||||||
|
namespace demangle {
|
||||||
|
|
||||||
|
// Returns a |malloc|-allocated C string with the demangled name.
|
||||||
|
// Returns an empty pointer if demangling was unsuccessful.
|
||||||
|
std::unique_ptr<char, base::FreeDeleter> Demangle(const char* mangled_name);
|
||||||
|
|
||||||
|
} // namespace demangle
|
||||||
|
} // namespace trace_processor
|
||||||
|
} // namespace perfetto
|
||||||
|
|
||||||
|
#endif // INCLUDE_PERFETTO_EXT_TRACE_PROCESSOR_DEMANGLE_H_
|
63
src/libtracing/perfetto/ext/trace_processor/export_json.h
Normal file
63
src/libtracing/perfetto/ext/trace_processor/export_json.h
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* 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_TRACE_PROCESSOR_EXPORT_JSON_H_
|
||||||
|
#define INCLUDE_PERFETTO_EXT_TRACE_PROCESSOR_EXPORT_JSON_H_
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include "perfetto/base/export.h"
|
||||||
|
#include "perfetto/trace_processor/status.h"
|
||||||
|
|
||||||
|
namespace perfetto {
|
||||||
|
namespace trace_processor {
|
||||||
|
|
||||||
|
class TraceProcessorStorage;
|
||||||
|
|
||||||
|
namespace json {
|
||||||
|
|
||||||
|
using ArgumentNameFilterPredicate = std::function<bool(const char* arg_name)>;
|
||||||
|
using ArgumentFilterPredicate =
|
||||||
|
std::function<bool(const char* category_group_name,
|
||||||
|
const char* event_name,
|
||||||
|
ArgumentNameFilterPredicate*)>;
|
||||||
|
using MetadataFilterPredicate = std::function<bool(const char* metadata_name)>;
|
||||||
|
using LabelFilterPredicate = std::function<bool(const char* label_name)>;
|
||||||
|
|
||||||
|
class PERFETTO_EXPORT_COMPONENT OutputWriter {
|
||||||
|
public:
|
||||||
|
OutputWriter();
|
||||||
|
virtual ~OutputWriter();
|
||||||
|
|
||||||
|
virtual util::Status AppendString(const std::string&) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Public for Chrome. Exports the trace loaded in TraceProcessorStorage to json,
|
||||||
|
// applying argument, metadata and label filtering using the callbacks.
|
||||||
|
util::Status PERFETTO_EXPORT_COMPONENT
|
||||||
|
ExportJson(TraceProcessorStorage*,
|
||||||
|
OutputWriter*,
|
||||||
|
ArgumentFilterPredicate = nullptr,
|
||||||
|
MetadataFilterPredicate = nullptr,
|
||||||
|
LabelFilterPredicate = nullptr);
|
||||||
|
|
||||||
|
} // namespace json
|
||||||
|
} // namespace trace_processor
|
||||||
|
} // namespace perfetto
|
||||||
|
|
||||||
|
#endif // INCLUDE_PERFETTO_EXT_TRACE_PROCESSOR_EXPORT_JSON_H_
|
|
@ -0,0 +1,31 @@
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import("../../../../../../gn/perfetto.gni")
|
||||||
|
|
||||||
|
source_set("memory_tracker") {
|
||||||
|
deps = [ "../../../../../../gn:default_deps" ]
|
||||||
|
public_deps = [
|
||||||
|
"../../../../base",
|
||||||
|
"../../../base",
|
||||||
|
]
|
||||||
|
sources = [
|
||||||
|
"graph.h",
|
||||||
|
"graph_processor.h",
|
||||||
|
"memory_allocator_node_id.h",
|
||||||
|
"memory_graph_edge.h",
|
||||||
|
"raw_memory_graph_node.h",
|
||||||
|
"raw_process_memory_node.h",
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,316 @@
|
||||||
|
/*
|
||||||
|
* 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_TRACE_PROCESSOR_IMPORTERS_MEMORY_TRACKER_GRAPH_H_
|
||||||
|
#define INCLUDE_PERFETTO_EXT_TRACE_PROCESSOR_IMPORTERS_MEMORY_TRACKER_GRAPH_H_
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <forward_list>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <set>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "perfetto/base/export.h"
|
||||||
|
#include "perfetto/base/proc_utils.h"
|
||||||
|
#include "perfetto/ext/base/string_utils.h"
|
||||||
|
#include "perfetto/ext/trace_processor/importers/memory_tracker/memory_allocator_node_id.h"
|
||||||
|
|
||||||
|
namespace perfetto {
|
||||||
|
namespace trace_processor {
|
||||||
|
|
||||||
|
const base::PlatformProcessId kNullProcessId = 0;
|
||||||
|
|
||||||
|
// Contains processed node graphs for each process and in the global space.
|
||||||
|
// This class is also the arena which owns the nodes of the graph.
|
||||||
|
class PERFETTO_EXPORT_COMPONENT GlobalNodeGraph {
|
||||||
|
public:
|
||||||
|
class Node;
|
||||||
|
class Edge;
|
||||||
|
class PreOrderIterator;
|
||||||
|
class PostOrderIterator;
|
||||||
|
|
||||||
|
// Graph of nodes either associated with a process or with
|
||||||
|
// the shared space.
|
||||||
|
class PERFETTO_EXPORT_COMPONENT Process {
|
||||||
|
public:
|
||||||
|
Process(base::PlatformProcessId pid, GlobalNodeGraph* global_graph);
|
||||||
|
~Process();
|
||||||
|
|
||||||
|
// Creates a node in the node graph which is associated with the
|
||||||
|
// given |id|, |path| and |weak|ness and returns it.
|
||||||
|
GlobalNodeGraph::Node* CreateNode(MemoryAllocatorNodeId id,
|
||||||
|
const std::string& path,
|
||||||
|
bool weak);
|
||||||
|
|
||||||
|
// Returns the node in the graph at the given |path| or nullptr
|
||||||
|
// if no such node exists in the provided |graph|.
|
||||||
|
GlobalNodeGraph::Node* FindNode(const std::string& path);
|
||||||
|
|
||||||
|
base::PlatformProcessId pid() const { return pid_; }
|
||||||
|
GlobalNodeGraph* global_graph() const { return global_graph_; }
|
||||||
|
GlobalNodeGraph::Node* root() const { return root_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
base::PlatformProcessId pid_;
|
||||||
|
GlobalNodeGraph* global_graph_;
|
||||||
|
GlobalNodeGraph::Node* root_;
|
||||||
|
Process(const Process&) = delete;
|
||||||
|
Process& operator=(const Process&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
// A single node in the graph of allocator nodes associated with a
|
||||||
|
// certain path and containing the entries for this path.
|
||||||
|
class PERFETTO_EXPORT_COMPONENT Node {
|
||||||
|
public:
|
||||||
|
// Auxilary data (a scalar number or a string) about this node each
|
||||||
|
// associated with a key.
|
||||||
|
struct PERFETTO_EXPORT_COMPONENT Entry {
|
||||||
|
enum Type {
|
||||||
|
kUInt64,
|
||||||
|
kString,
|
||||||
|
};
|
||||||
|
|
||||||
|
// The units of the entry if the entry is a scalar. The scalar
|
||||||
|
// refers to either a number of objects or a size in bytes.
|
||||||
|
enum ScalarUnits {
|
||||||
|
kObjects,
|
||||||
|
kBytes,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Creates the entry with the appropriate type.
|
||||||
|
Entry(ScalarUnits units, uint64_t value);
|
||||||
|
explicit Entry(const std::string& value);
|
||||||
|
|
||||||
|
const Type type;
|
||||||
|
const ScalarUnits units;
|
||||||
|
|
||||||
|
// The value of the entry if this entry has a string type.
|
||||||
|
const std::string value_string;
|
||||||
|
|
||||||
|
// The value of the entry if this entry has a integer type.
|
||||||
|
const uint64_t value_uint64;
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit Node(GlobalNodeGraph::Process* node_graph, Node* parent);
|
||||||
|
~Node();
|
||||||
|
|
||||||
|
// Gets the direct child of a node for the given |subpath|.
|
||||||
|
Node* GetChild(const std::string& name) const;
|
||||||
|
|
||||||
|
// Inserts the given |node| as a child of the current node
|
||||||
|
// with the given |subpath| as the key.
|
||||||
|
void InsertChild(const std::string& name, Node* node);
|
||||||
|
|
||||||
|
// Creates a child for this node with the given |name| as the key.
|
||||||
|
Node* CreateChild(const std::string& name);
|
||||||
|
|
||||||
|
// Checks if the current node is a descendent (i.e. exists as a child,
|
||||||
|
// child of a child, etc.) of the given node |possible_parent|.
|
||||||
|
bool IsDescendentOf(const Node& possible_parent) const;
|
||||||
|
|
||||||
|
// Adds an entry for this node node with the given |name|, |units| and
|
||||||
|
// type.
|
||||||
|
void AddEntry(const std::string& name,
|
||||||
|
Entry::ScalarUnits units,
|
||||||
|
uint64_t value);
|
||||||
|
void AddEntry(const std::string& name, const std::string& value);
|
||||||
|
|
||||||
|
// Adds an edge which indicates that this node is owned by
|
||||||
|
// another node.
|
||||||
|
void AddOwnedByEdge(Edge* edge);
|
||||||
|
|
||||||
|
// Sets the edge indicates that this node owns another node.
|
||||||
|
void SetOwnsEdge(Edge* edge);
|
||||||
|
|
||||||
|
bool is_weak() const { return weak_; }
|
||||||
|
void set_weak(bool weak) { weak_ = weak; }
|
||||||
|
bool is_explicit() const { return explicit_; }
|
||||||
|
void set_explicit(bool explicit_node) { explicit_ = explicit_node; }
|
||||||
|
uint64_t not_owned_sub_size() const { return not_owned_sub_size_; }
|
||||||
|
void add_not_owned_sub_size(uint64_t addition) {
|
||||||
|
not_owned_sub_size_ += addition;
|
||||||
|
}
|
||||||
|
uint64_t not_owning_sub_size() const { return not_owning_sub_size_; }
|
||||||
|
void add_not_owning_sub_size(uint64_t addition) {
|
||||||
|
not_owning_sub_size_ += addition;
|
||||||
|
}
|
||||||
|
double owned_coefficient() const { return owned_coefficient_; }
|
||||||
|
void set_owned_coefficient(double owned_coefficient) {
|
||||||
|
owned_coefficient_ = owned_coefficient;
|
||||||
|
}
|
||||||
|
double owning_coefficient() const { return owning_coefficient_; }
|
||||||
|
void set_owning_coefficient(double owning_coefficient) {
|
||||||
|
owning_coefficient_ = owning_coefficient;
|
||||||
|
}
|
||||||
|
double cumulative_owned_coefficient() const {
|
||||||
|
return cumulative_owned_coefficient_;
|
||||||
|
}
|
||||||
|
void set_cumulative_owned_coefficient(double cumulative_owned_coefficient) {
|
||||||
|
cumulative_owned_coefficient_ = cumulative_owned_coefficient;
|
||||||
|
}
|
||||||
|
double cumulative_owning_coefficient() const {
|
||||||
|
return cumulative_owning_coefficient_;
|
||||||
|
}
|
||||||
|
void set_cumulative_owning_coefficient(
|
||||||
|
double cumulative_owning_coefficient) {
|
||||||
|
cumulative_owning_coefficient_ = cumulative_owning_coefficient;
|
||||||
|
}
|
||||||
|
MemoryAllocatorNodeId id() const { return id_; }
|
||||||
|
void set_id(MemoryAllocatorNodeId id) { id_ = id; }
|
||||||
|
GlobalNodeGraph::Edge* owns_edge() const { return owns_edge_; }
|
||||||
|
std::map<std::string, Node*>* children() { return &children_; }
|
||||||
|
const std::map<std::string, Node*>& const_children() const {
|
||||||
|
return children_;
|
||||||
|
}
|
||||||
|
std::vector<GlobalNodeGraph::Edge*>* owned_by_edges() {
|
||||||
|
return &owned_by_edges_;
|
||||||
|
}
|
||||||
|
const Node* parent() const { return parent_; }
|
||||||
|
const GlobalNodeGraph::Process* node_graph() const { return node_graph_; }
|
||||||
|
std::map<std::string, Entry>* entries() { return &entries_; }
|
||||||
|
const std::map<std::string, Entry>& const_entries() const {
|
||||||
|
return entries_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
GlobalNodeGraph::Process* node_graph_;
|
||||||
|
Node* const parent_;
|
||||||
|
MemoryAllocatorNodeId id_;
|
||||||
|
std::map<std::string, Entry> entries_;
|
||||||
|
std::map<std::string, Node*> children_;
|
||||||
|
bool explicit_ = false;
|
||||||
|
bool weak_ = false;
|
||||||
|
uint64_t not_owning_sub_size_ = 0;
|
||||||
|
uint64_t not_owned_sub_size_ = 0;
|
||||||
|
double owned_coefficient_ = 1;
|
||||||
|
double owning_coefficient_ = 1;
|
||||||
|
double cumulative_owned_coefficient_ = 1;
|
||||||
|
double cumulative_owning_coefficient_ = 1;
|
||||||
|
|
||||||
|
GlobalNodeGraph::Edge* owns_edge_;
|
||||||
|
std::vector<GlobalNodeGraph::Edge*> owned_by_edges_;
|
||||||
|
|
||||||
|
Node(const Node&) = delete;
|
||||||
|
Node& operator=(const Node&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
// An edge in the node graph which indicates ownership between the
|
||||||
|
// source and target nodes.
|
||||||
|
class PERFETTO_EXPORT_COMPONENT Edge {
|
||||||
|
public:
|
||||||
|
Edge(GlobalNodeGraph::Node* source,
|
||||||
|
GlobalNodeGraph::Node* target,
|
||||||
|
int priority);
|
||||||
|
|
||||||
|
GlobalNodeGraph::Node* source() const { return source_; }
|
||||||
|
GlobalNodeGraph::Node* target() const { return target_; }
|
||||||
|
int priority() const { return priority_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
GlobalNodeGraph::Node* const source_;
|
||||||
|
GlobalNodeGraph::Node* const target_;
|
||||||
|
const int priority_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// An iterator-esque class which yields nodes in a depth-first pre order.
|
||||||
|
class PERFETTO_EXPORT_COMPONENT PreOrderIterator {
|
||||||
|
public:
|
||||||
|
explicit PreOrderIterator(std::vector<Node*>&& root_nodes);
|
||||||
|
PreOrderIterator(PreOrderIterator&& other);
|
||||||
|
~PreOrderIterator();
|
||||||
|
|
||||||
|
// Yields the next node in the DFS post-order traversal.
|
||||||
|
Node* next();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Node*> to_visit_;
|
||||||
|
std::set<const Node*> visited_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// An iterator-esque class which yields nodes in a depth-first post order.
|
||||||
|
class PERFETTO_EXPORT_COMPONENT PostOrderIterator {
|
||||||
|
public:
|
||||||
|
explicit PostOrderIterator(std::vector<Node*>&& root_nodes);
|
||||||
|
PostOrderIterator(PostOrderIterator&& other);
|
||||||
|
~PostOrderIterator();
|
||||||
|
|
||||||
|
// Yields the next node in the DFS post-order traversal.
|
||||||
|
Node* next();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Node*> to_visit_;
|
||||||
|
std::set<Node*> visited_;
|
||||||
|
std::vector<Node*> path_;
|
||||||
|
};
|
||||||
|
|
||||||
|
using ProcessNodeGraphMap =
|
||||||
|
std::map<base::PlatformProcessId,
|
||||||
|
std::unique_ptr<GlobalNodeGraph::Process>>;
|
||||||
|
using IdNodeMap = std::map<MemoryAllocatorNodeId, Node*>;
|
||||||
|
|
||||||
|
GlobalNodeGraph();
|
||||||
|
~GlobalNodeGraph();
|
||||||
|
|
||||||
|
// Creates a container for all the node graphs for the process given
|
||||||
|
// by the given |process_id|.
|
||||||
|
GlobalNodeGraph::Process* CreateGraphForProcess(
|
||||||
|
base::PlatformProcessId process_id);
|
||||||
|
|
||||||
|
// Adds an edge in the node graph with the given source and target nodes
|
||||||
|
// and edge priority.
|
||||||
|
void AddNodeOwnershipEdge(Node* owner, Node* owned, int priority);
|
||||||
|
|
||||||
|
// Returns an iterator which yields nodes in the nodes in this graph in
|
||||||
|
// pre-order. That is, children and owners of nodes are returned after the
|
||||||
|
// node itself.
|
||||||
|
PreOrderIterator VisitInDepthFirstPreOrder();
|
||||||
|
|
||||||
|
// Returns an iterator which yields nodes in the nodes in this graph in
|
||||||
|
// post-order. That is, children and owners of nodes are returned before the
|
||||||
|
// node itself.
|
||||||
|
PostOrderIterator VisitInDepthFirstPostOrder();
|
||||||
|
|
||||||
|
const IdNodeMap& nodes_by_id() const { return nodes_by_id_; }
|
||||||
|
GlobalNodeGraph::Process* shared_memory_graph() const {
|
||||||
|
return shared_memory_graph_.get();
|
||||||
|
}
|
||||||
|
const ProcessNodeGraphMap& process_node_graphs() const {
|
||||||
|
return process_node_graphs_;
|
||||||
|
}
|
||||||
|
const std::forward_list<Edge>& edges() const { return all_edges_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Creates a node in the arena which is associated with the given
|
||||||
|
// |process_graph| and for the given |parent|.
|
||||||
|
Node* CreateNode(GlobalNodeGraph::Process* process_graph,
|
||||||
|
GlobalNodeGraph::Node* parent);
|
||||||
|
|
||||||
|
std::forward_list<Node> all_nodes_;
|
||||||
|
std::forward_list<Edge> all_edges_;
|
||||||
|
IdNodeMap nodes_by_id_;
|
||||||
|
std::unique_ptr<GlobalNodeGraph::Process> shared_memory_graph_;
|
||||||
|
ProcessNodeGraphMap process_node_graphs_;
|
||||||
|
GlobalNodeGraph(const GlobalNodeGraph&) = delete;
|
||||||
|
GlobalNodeGraph& operator=(const GlobalNodeGraph&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace trace_processor
|
||||||
|
} // namespace perfetto
|
||||||
|
|
||||||
|
#endif // INCLUDE_PERFETTO_EXT_TRACE_PROCESSOR_IMPORTERS_MEMORY_TRACKER_GRAPH_H_
|
|
@ -0,0 +1,250 @@
|
||||||
|
/*
|
||||||
|
* 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_TRACE_PROCESSOR_IMPORTERS_MEMORY_TRACKER_GRAPH_PROCESSOR_H_
|
||||||
|
#define INCLUDE_PERFETTO_EXT_TRACE_PROCESSOR_IMPORTERS_MEMORY_TRACKER_GRAPH_PROCESSOR_H_
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <set>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "perfetto/base/proc_utils.h"
|
||||||
|
#include "perfetto/ext/trace_processor/importers/memory_tracker/graph.h"
|
||||||
|
#include "perfetto/ext/trace_processor/importers/memory_tracker/raw_process_memory_node.h"
|
||||||
|
|
||||||
|
namespace perfetto {
|
||||||
|
namespace trace_processor {
|
||||||
|
|
||||||
|
class PERFETTO_EXPORT_COMPONENT GraphProcessor {
|
||||||
|
public:
|
||||||
|
// This map does not own the pointers inside.
|
||||||
|
using RawMemoryNodeMap =
|
||||||
|
std::map<base::PlatformProcessId, std::unique_ptr<RawProcessMemoryNode>>;
|
||||||
|
|
||||||
|
static std::unique_ptr<GlobalNodeGraph> CreateMemoryGraph(
|
||||||
|
const RawMemoryNodeMap& process_nodes);
|
||||||
|
|
||||||
|
static void RemoveWeakNodesFromGraph(GlobalNodeGraph* global_graph);
|
||||||
|
|
||||||
|
static void AddOverheadsAndPropagateEntries(GlobalNodeGraph* global_graph);
|
||||||
|
|
||||||
|
static void CalculateSizesForGraph(GlobalNodeGraph* global_graph);
|
||||||
|
|
||||||
|
static std::map<base::PlatformProcessId, uint64_t>
|
||||||
|
ComputeSharedFootprintFromGraph(const GlobalNodeGraph& global_graph);
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class GraphProcessorTest;
|
||||||
|
|
||||||
|
static void CollectAllocatorNodes(const RawProcessMemoryNode& source,
|
||||||
|
GlobalNodeGraph* global_graph,
|
||||||
|
GlobalNodeGraph::Process* process_graph);
|
||||||
|
|
||||||
|
static void AddEdges(const RawProcessMemoryNode& source,
|
||||||
|
GlobalNodeGraph* global_graph);
|
||||||
|
|
||||||
|
static void MarkImplicitWeakParentsRecursively(GlobalNodeGraph::Node* node);
|
||||||
|
|
||||||
|
static void MarkWeakOwnersAndChildrenRecursively(
|
||||||
|
GlobalNodeGraph::Node* node,
|
||||||
|
std::set<const GlobalNodeGraph::Node*>* nodes);
|
||||||
|
|
||||||
|
static void RemoveWeakNodesRecursively(GlobalNodeGraph::Node* parent);
|
||||||
|
|
||||||
|
static void AssignTracingOverhead(const std::string& allocator,
|
||||||
|
GlobalNodeGraph* global_graph,
|
||||||
|
GlobalNodeGraph::Process* process);
|
||||||
|
|
||||||
|
static GlobalNodeGraph::Node::Entry AggregateNumericWithNameForNode(
|
||||||
|
GlobalNodeGraph::Node* node,
|
||||||
|
const std::string& name);
|
||||||
|
|
||||||
|
static void AggregateNumericsRecursively(GlobalNodeGraph::Node* node);
|
||||||
|
|
||||||
|
static void PropagateNumericsAndDiagnosticsRecursively(
|
||||||
|
GlobalNodeGraph::Node* node);
|
||||||
|
|
||||||
|
static std::optional<uint64_t> AggregateSizeForDescendantNode(
|
||||||
|
GlobalNodeGraph::Node* root,
|
||||||
|
GlobalNodeGraph::Node* descendant);
|
||||||
|
|
||||||
|
static void CalculateSizeForNode(GlobalNodeGraph::Node* node);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate not-owned and not-owning sub-sizes of a memory allocator node
|
||||||
|
* from its children's (sub-)sizes.
|
||||||
|
*
|
||||||
|
* Not-owned sub-size refers to the aggregated memory of all children which
|
||||||
|
* is not owned by other MADs. Conversely, not-owning sub-size is the
|
||||||
|
* aggregated memory of all children which do not own another MAD. The
|
||||||
|
* diagram below illustrates these two concepts:
|
||||||
|
*
|
||||||
|
* ROOT 1 ROOT 2
|
||||||
|
* size: 4 size: 5
|
||||||
|
* not-owned sub-size: 4 not-owned sub-size: 1 (!)
|
||||||
|
* not-owning sub-size: 0 (!) not-owning sub-size: 5
|
||||||
|
*
|
||||||
|
* ^ ^
|
||||||
|
* | |
|
||||||
|
*
|
||||||
|
* PARENT 1 ===== owns =====> PARENT 2
|
||||||
|
* size: 4 size: 5
|
||||||
|
* not-owned sub-size: 4 not-owned sub-size: 5
|
||||||
|
* not-owning sub-size: 4 not-owning sub-size: 5
|
||||||
|
*
|
||||||
|
* ^ ^
|
||||||
|
* | |
|
||||||
|
*
|
||||||
|
* CHILD 1 CHILD 2
|
||||||
|
* size [given]: 4 size [given]: 5
|
||||||
|
* not-owned sub-size: 4 not-owned sub-size: 5
|
||||||
|
* not-owning sub-size: 4 not-owning sub-size: 5
|
||||||
|
*
|
||||||
|
* This method assumes that (1) the size of the node, its children, and its
|
||||||
|
* owners [see calculateSizes()] and (2) the not-owned and not-owning
|
||||||
|
* sub-sizes of both the children and owners of the node have already been
|
||||||
|
* calculated [depth-first post-order traversal].
|
||||||
|
*/
|
||||||
|
static void CalculateNodeSubSizes(GlobalNodeGraph::Node* node);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate owned and owning coefficients of a memory allocator node and
|
||||||
|
* its owners.
|
||||||
|
*
|
||||||
|
* The owning coefficient refers to the proportion of a node's not-owning
|
||||||
|
* sub-size which is attributed to the node (only relevant to owning MADs).
|
||||||
|
* Conversely, the owned coefficient is the proportion of a node's
|
||||||
|
* not-owned sub-size, which is attributed to it (only relevant to owned
|
||||||
|
* MADs).
|
||||||
|
*
|
||||||
|
* The not-owned size of the owned node is split among its owners in the
|
||||||
|
* order of the ownership importance as demonstrated by the following
|
||||||
|
* example:
|
||||||
|
*
|
||||||
|
* memory allocator nodes
|
||||||
|
* OWNED OWNER1 OWNER2 OWNER3 OWNER4
|
||||||
|
* not-owned sub-size [given] 10 - - - -
|
||||||
|
* not-owning sub-size [given] - 6 7 5 8
|
||||||
|
* importance [given] - 2 2 1 0
|
||||||
|
* attributed not-owned sub-size 2 - - - -
|
||||||
|
* attributed not-owning sub-size - 3 4 0 1
|
||||||
|
* owned coefficient 2/10 - - - -
|
||||||
|
* owning coefficient - 3/6 4/7 0/5 1/8
|
||||||
|
*
|
||||||
|
* Explanation: Firstly, 6 bytes are split equally among OWNER1 and OWNER2
|
||||||
|
* (highest importance). OWNER2 owns one more byte, so its attributed
|
||||||
|
* not-owning sub-size is 6/2 + 1 = 4 bytes. OWNER3 is attributed no size
|
||||||
|
* because it is smaller than the owners with higher priority. However,
|
||||||
|
* OWNER4 is larger, so it's attributed the difference 8 - 7 = 1 byte.
|
||||||
|
* Finally, 2 bytes remain unattributed and are hence kept in the OWNED
|
||||||
|
* node as attributed not-owned sub-size. The coefficients are then
|
||||||
|
* directly calculated as fractions of the sub-sizes and corresponding
|
||||||
|
* attributed sub-sizes.
|
||||||
|
*
|
||||||
|
* Note that we always assume that all ownerships of a node overlap (e.g.
|
||||||
|
* OWNER3 is subsumed by both OWNER1 and OWNER2). Hence, the table could
|
||||||
|
* be alternatively represented as follows:
|
||||||
|
*
|
||||||
|
* owned memory range
|
||||||
|
* 0 1 2 3 4 5 6 7 8 9 10
|
||||||
|
* Priority 2 | OWNER1 + OWNER2 (split) | OWNER2 |
|
||||||
|
* Priority 1 | (already attributed) |
|
||||||
|
* Priority 0 | - - - (already attributed) - - - | OWNER4 |
|
||||||
|
* Remainder | - - - - - (already attributed) - - - - - - | OWNED |
|
||||||
|
*
|
||||||
|
* This method assumes that (1) the size of the node [see calculateSizes()]
|
||||||
|
* and (2) the not-owned size of the node and not-owning sub-sizes of its
|
||||||
|
* owners [see the first step of calculateEffectiveSizes()] have already
|
||||||
|
* been calculated. Note that the method doesn't make any assumptions about
|
||||||
|
* the order in which nodes are visited.
|
||||||
|
*/
|
||||||
|
static void CalculateNodeOwnershipCoefficient(GlobalNodeGraph::Node* node);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate cumulative owned and owning coefficients of a memory allocator
|
||||||
|
* node from its (non-cumulative) owned and owning coefficients and the
|
||||||
|
* cumulative coefficients of its parent and/or owned node.
|
||||||
|
*
|
||||||
|
* The cumulative coefficients represent the total effect of all
|
||||||
|
* (non-strict) ancestor ownerships on a memory allocator node. The
|
||||||
|
* cumulative owned coefficient of a MAD can be calculated simply as:
|
||||||
|
*
|
||||||
|
* cumulativeOwnedC(M) = ownedC(M) * cumulativeOwnedC(parent(M))
|
||||||
|
*
|
||||||
|
* This reflects the assumption that if a parent of a child MAD is
|
||||||
|
* (partially) owned, then the parent's owner also indirectly owns (a part
|
||||||
|
* of) the child MAD.
|
||||||
|
*
|
||||||
|
* The cumulative owning coefficient of a MAD depends on whether the MAD
|
||||||
|
* owns another node:
|
||||||
|
*
|
||||||
|
* [if M doesn't own another MAD]
|
||||||
|
* / cumulativeOwningC(parent(M))
|
||||||
|
* cumulativeOwningC(M) =
|
||||||
|
* \ [if M owns another MAD]
|
||||||
|
* owningC(M) * cumulativeOwningC(owned(M))
|
||||||
|
*
|
||||||
|
* The reasoning behind the first case is similar to the one for cumulative
|
||||||
|
* owned coefficient above. The only difference is that we don't need to
|
||||||
|
* include the node's (non-cumulative) owning coefficient because it is
|
||||||
|
* implicitly 1.
|
||||||
|
*
|
||||||
|
* The formula for the second case is derived as follows: Since the MAD
|
||||||
|
* owns another node, its memory is not included in its parent's not-owning
|
||||||
|
* sub-size and hence shouldn't be affected by the parent's corresponding
|
||||||
|
* cumulative coefficient. Instead, the MAD indirectly owns everything
|
||||||
|
* owned by its owned node (and so it should be affected by the
|
||||||
|
* corresponding coefficient).
|
||||||
|
*
|
||||||
|
* Note that undefined coefficients (and coefficients of non-existent
|
||||||
|
* nodes) are implicitly assumed to be 1.
|
||||||
|
*
|
||||||
|
* This method assumes that (1) the size of the node [see calculateSizes()],
|
||||||
|
* (2) the (non-cumulative) owned and owning coefficients of the node [see
|
||||||
|
* the second step of calculateEffectiveSizes()], and (3) the cumulative
|
||||||
|
* coefficients of the node's parent and owned MADs (if present)
|
||||||
|
* [depth-first pre-order traversal] have already been calculated.
|
||||||
|
*/
|
||||||
|
static void CalculateNodeCumulativeOwnershipCoefficient(
|
||||||
|
GlobalNodeGraph::Node* node);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the effective size of a memory allocator node.
|
||||||
|
*
|
||||||
|
* In order to simplify the (already complex) calculation, we use the fact
|
||||||
|
* that effective size is cumulative (unlike regular size), i.e. the
|
||||||
|
* effective size of a non-leaf node is equal to the sum of effective sizes
|
||||||
|
* of its children. The effective size of a leaf MAD is calculated as:
|
||||||
|
*
|
||||||
|
* effectiveSize(M) = size(M) * cumulativeOwningC(M) * cumulativeOwnedC(M)
|
||||||
|
*
|
||||||
|
* This method assumes that (1) the size of the node and its children [see
|
||||||
|
* calculateSizes()] and (2) the cumulative owning and owned coefficients
|
||||||
|
* of the node (if it's a leaf node) [see the third step of
|
||||||
|
* calculateEffectiveSizes()] or the effective sizes of its children (if
|
||||||
|
* it's a non-leaf node) [depth-first post-order traversal] have already
|
||||||
|
* been calculated.
|
||||||
|
*/
|
||||||
|
static void CalculateNodeEffectiveSize(GlobalNodeGraph::Node* node);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace trace_processor
|
||||||
|
} // namespace perfetto
|
||||||
|
|
||||||
|
#endif // INCLUDE_PERFETTO_EXT_TRACE_PROCESSOR_IMPORTERS_MEMORY_TRACKER_GRAPH_PROCESSOR_H_
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* 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_TRACE_PROCESSOR_IMPORTERS_MEMORY_TRACKER_MEMORY_ALLOCATOR_NODE_ID_H_
|
||||||
|
#define INCLUDE_PERFETTO_EXT_TRACE_PROCESSOR_IMPORTERS_MEMORY_TRACKER_MEMORY_ALLOCATOR_NODE_ID_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "perfetto/base/export.h"
|
||||||
|
|
||||||
|
namespace perfetto {
|
||||||
|
namespace trace_processor {
|
||||||
|
|
||||||
|
class PERFETTO_EXPORT_COMPONENT MemoryAllocatorNodeId {
|
||||||
|
public:
|
||||||
|
MemoryAllocatorNodeId();
|
||||||
|
explicit MemoryAllocatorNodeId(uint64_t id);
|
||||||
|
|
||||||
|
uint64_t ToUint64() const { return id_; }
|
||||||
|
|
||||||
|
// Returns a (hex-encoded) string representation of the id.
|
||||||
|
std::string ToString() const;
|
||||||
|
|
||||||
|
bool empty() const { return id_ == 0u; }
|
||||||
|
|
||||||
|
bool operator==(const MemoryAllocatorNodeId& other) const {
|
||||||
|
return id_ == other.id_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const MemoryAllocatorNodeId& other) const {
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<(const MemoryAllocatorNodeId& other) const {
|
||||||
|
return id_ < other.id_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint64_t id_;
|
||||||
|
|
||||||
|
// Deliberately copy-able.
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace trace_processor
|
||||||
|
} // namespace perfetto
|
||||||
|
|
||||||
|
#endif // INCLUDE_PERFETTO_EXT_TRACE_PROCESSOR_IMPORTERS_MEMORY_TRACKER_MEMORY_ALLOCATOR_NODE_ID_H_
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* 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_TRACE_PROCESSOR_IMPORTERS_MEMORY_TRACKER_MEMORY_GRAPH_EDGE_H_
|
||||||
|
#define INCLUDE_PERFETTO_EXT_TRACE_PROCESSOR_IMPORTERS_MEMORY_TRACKER_MEMORY_GRAPH_EDGE_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "perfetto/base/export.h"
|
||||||
|
#include "perfetto/ext/trace_processor/importers/memory_tracker/memory_allocator_node_id.h"
|
||||||
|
|
||||||
|
namespace perfetto {
|
||||||
|
namespace trace_processor {
|
||||||
|
|
||||||
|
class PERFETTO_EXPORT_COMPONENT MemoryGraphEdge {
|
||||||
|
public:
|
||||||
|
MemoryGraphEdge(MemoryAllocatorNodeId s,
|
||||||
|
MemoryAllocatorNodeId t,
|
||||||
|
int i,
|
||||||
|
bool o)
|
||||||
|
: source(s), target(t), importance(i), overridable(o) {}
|
||||||
|
|
||||||
|
MemoryGraphEdge& operator=(const MemoryGraphEdge& edge) {
|
||||||
|
source = edge.source;
|
||||||
|
target = edge.target;
|
||||||
|
importance = edge.importance;
|
||||||
|
overridable = edge.overridable;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoryAllocatorNodeId source;
|
||||||
|
MemoryAllocatorNodeId target;
|
||||||
|
int importance;
|
||||||
|
bool overridable;
|
||||||
|
|
||||||
|
// Deliberately copy-able.
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace trace_processor
|
||||||
|
} // namespace perfetto
|
||||||
|
|
||||||
|
#endif // INCLUDE_PERFETTO_EXT_TRACE_PROCESSOR_IMPORTERS_MEMORY_TRACKER_MEMORY_GRAPH_EDGE_H_
|
|
@ -0,0 +1,147 @@
|
||||||
|
/*
|
||||||
|
* 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_TRACE_PROCESSOR_IMPORTERS_MEMORY_TRACKER_RAW_MEMORY_GRAPH_NODE_H_
|
||||||
|
#define INCLUDE_PERFETTO_EXT_TRACE_PROCESSOR_IMPORTERS_MEMORY_TRACKER_RAW_MEMORY_GRAPH_NODE_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "perfetto/base/export.h"
|
||||||
|
#include "perfetto/ext/trace_processor/importers/memory_tracker/memory_allocator_node_id.h"
|
||||||
|
#include "perfetto/ext/trace_processor/importers/memory_tracker/memory_graph_edge.h"
|
||||||
|
|
||||||
|
namespace perfetto {
|
||||||
|
namespace trace_processor {
|
||||||
|
|
||||||
|
// Describes the level of detail of the memory graph.
|
||||||
|
enum class LevelOfDetail : uint32_t {
|
||||||
|
kFirst,
|
||||||
|
|
||||||
|
// For background tracing mode. The node time is quick, and typically just the
|
||||||
|
// totals are expected. Suballocations need not be specified. Node name must
|
||||||
|
// contain only pre-defined strings and string arguments cannot be added.
|
||||||
|
kBackground = kFirst,
|
||||||
|
|
||||||
|
// For the levels below, MemoryNodeProvider instances must guarantee that the
|
||||||
|
// total size reported in the root node is consistent. Only the granularity of
|
||||||
|
// the child MemoryAllocatorNode(s) differs with the levels.
|
||||||
|
|
||||||
|
// Few entries, typically a fixed number, per node.
|
||||||
|
kLight,
|
||||||
|
|
||||||
|
// Unrestricted amount of entries per node.
|
||||||
|
kDetailed,
|
||||||
|
|
||||||
|
kLast = kDetailed
|
||||||
|
};
|
||||||
|
|
||||||
|
// Data model for user-land memory nodes.
|
||||||
|
class PERFETTO_EXPORT_COMPONENT RawMemoryGraphNode {
|
||||||
|
public:
|
||||||
|
enum Flags {
|
||||||
|
kDefault = 0,
|
||||||
|
|
||||||
|
// A node marked weak will be discarded if there is no ownership edge exists
|
||||||
|
// from a non-weak node.
|
||||||
|
kWeak = 1 << 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// In the UI table each MemoryAllocatorNode becomes
|
||||||
|
// a row and each Entry generates a column (if it doesn't already
|
||||||
|
// exist).
|
||||||
|
struct PERFETTO_EXPORT_COMPONENT MemoryNodeEntry {
|
||||||
|
enum EntryType {
|
||||||
|
kUint64,
|
||||||
|
kString,
|
||||||
|
};
|
||||||
|
|
||||||
|
MemoryNodeEntry(const std::string& name,
|
||||||
|
const std::string& units,
|
||||||
|
uint64_t value);
|
||||||
|
MemoryNodeEntry(const std::string& name,
|
||||||
|
const std::string& units,
|
||||||
|
const std::string& value);
|
||||||
|
|
||||||
|
bool operator==(const MemoryNodeEntry& rhs) const;
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
std::string units;
|
||||||
|
|
||||||
|
EntryType entry_type;
|
||||||
|
|
||||||
|
uint64_t value_uint64;
|
||||||
|
std::string value_string;
|
||||||
|
};
|
||||||
|
|
||||||
|
RawMemoryGraphNode(const std::string& absolute_name,
|
||||||
|
LevelOfDetail level,
|
||||||
|
MemoryAllocatorNodeId id);
|
||||||
|
|
||||||
|
RawMemoryGraphNode(
|
||||||
|
const std::string& absolute_name,
|
||||||
|
LevelOfDetail level,
|
||||||
|
MemoryAllocatorNodeId id,
|
||||||
|
std::vector<RawMemoryGraphNode::MemoryNodeEntry>&& entries);
|
||||||
|
|
||||||
|
// Standard attribute |name|s for the AddScalar and AddString() methods.
|
||||||
|
static const char kNameSize[]; // To represent allocated space.
|
||||||
|
static const char kNameObjectCount[]; // To represent number of objects.
|
||||||
|
|
||||||
|
// Standard attribute |unit|s for the AddScalar and AddString() methods.
|
||||||
|
static const char kUnitsBytes[]; // Unit name to represent bytes.
|
||||||
|
static const char kUnitsObjects[]; // Unit name to represent #objects.
|
||||||
|
|
||||||
|
// Constants used only internally and by tests.
|
||||||
|
static const char kTypeScalar[]; // Type name for scalar attributes.
|
||||||
|
static const char kTypeString[]; // Type name for string attributes.
|
||||||
|
|
||||||
|
// |id| is an optional global node identifier, unique across all processes
|
||||||
|
// within the scope of a global node.
|
||||||
|
// Subsequent MemoryAllocatorNode(s) with the same |absolute_name| are
|
||||||
|
// expected to have the same id.
|
||||||
|
MemoryAllocatorNodeId id() const { return id_; }
|
||||||
|
|
||||||
|
// Absolute name, unique within the scope of an entire ProcessMemoryNode.
|
||||||
|
const std::string& absolute_name() const { return absolute_name_; }
|
||||||
|
|
||||||
|
const std::vector<MemoryNodeEntry>& entries() const { return entries_; }
|
||||||
|
|
||||||
|
LevelOfDetail level_of_detail() const { return level_of_detail_; }
|
||||||
|
|
||||||
|
// Use enum Flags to set values.
|
||||||
|
void set_flags(int flags) { flags_ |= flags; }
|
||||||
|
void clear_flags(int flags) { flags_ &= ~flags; }
|
||||||
|
int flags() const { return flags_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string absolute_name_;
|
||||||
|
LevelOfDetail level_of_detail_;
|
||||||
|
std::vector<MemoryNodeEntry> entries_;
|
||||||
|
MemoryAllocatorNodeId id_;
|
||||||
|
|
||||||
|
// A node marked weak will be discarded by TraceViewer.
|
||||||
|
int flags_; // See enum Flags.
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace trace_processor
|
||||||
|
} // namespace perfetto
|
||||||
|
|
||||||
|
#endif // INCLUDE_PERFETTO_EXT_TRACE_PROCESSOR_IMPORTERS_MEMORY_TRACKER_RAW_MEMORY_GRAPH_NODE_H_
|
|
@ -0,0 +1,86 @@
|
||||||
|
/*
|
||||||
|
* 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_TRACE_PROCESSOR_IMPORTERS_MEMORY_TRACKER_RAW_PROCESS_MEMORY_NODE_H_
|
||||||
|
#define INCLUDE_PERFETTO_EXT_TRACE_PROCESSOR_IMPORTERS_MEMORY_TRACKER_RAW_PROCESS_MEMORY_NODE_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "perfetto/base/export.h"
|
||||||
|
#include "perfetto/ext/trace_processor/importers/memory_tracker/memory_allocator_node_id.h"
|
||||||
|
#include "perfetto/ext/trace_processor/importers/memory_tracker/memory_graph_edge.h"
|
||||||
|
#include "perfetto/ext/trace_processor/importers/memory_tracker/raw_memory_graph_node.h"
|
||||||
|
|
||||||
|
namespace perfetto {
|
||||||
|
namespace trace_processor {
|
||||||
|
|
||||||
|
// ProcessMemoryNode is as a strongly typed container which holds the nodes
|
||||||
|
// produced by the MemoryNodeProvider(s) for a specific process.
|
||||||
|
class PERFETTO_EXPORT_COMPONENT RawProcessMemoryNode {
|
||||||
|
public:
|
||||||
|
// Maps allocator nodes absolute names (allocator_name/heap/subheap) to
|
||||||
|
// MemoryAllocatorNode instances.
|
||||||
|
using MemoryNodesMap =
|
||||||
|
std::map<std::string, std::unique_ptr<RawMemoryGraphNode>>;
|
||||||
|
|
||||||
|
// Stores allocator node edges indexed by source allocator node GUID.
|
||||||
|
using AllocatorNodeEdgesMap =
|
||||||
|
std::map<MemoryAllocatorNodeId, std::unique_ptr<MemoryGraphEdge>>;
|
||||||
|
|
||||||
|
explicit RawProcessMemoryNode(
|
||||||
|
LevelOfDetail level_of_detail,
|
||||||
|
AllocatorNodeEdgesMap&& edges_map = AllocatorNodeEdgesMap{},
|
||||||
|
MemoryNodesMap&& nodes_map = MemoryNodesMap{});
|
||||||
|
RawProcessMemoryNode(RawProcessMemoryNode&&);
|
||||||
|
~RawProcessMemoryNode();
|
||||||
|
RawProcessMemoryNode& operator=(RawProcessMemoryNode&&);
|
||||||
|
|
||||||
|
// Looks up a MemoryAllocatorNode given its allocator and heap names, or
|
||||||
|
// nullptr if not found.
|
||||||
|
RawMemoryGraphNode* GetAllocatorNode(const std::string& absolute_name) const;
|
||||||
|
|
||||||
|
// Returns the map of the MemoryAllocatorNodes added to this node.
|
||||||
|
const MemoryNodesMap& allocator_nodes() const { return allocator_nodes_; }
|
||||||
|
|
||||||
|
const AllocatorNodeEdgesMap& allocator_nodes_edges() const {
|
||||||
|
return allocator_nodes_edges_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const LevelOfDetail& level_of_detail() const { return level_of_detail_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
LevelOfDetail level_of_detail_;
|
||||||
|
|
||||||
|
// Keeps track of relationships between MemoryAllocatorNode(s).
|
||||||
|
AllocatorNodeEdgesMap allocator_nodes_edges_;
|
||||||
|
|
||||||
|
// Level of detail of the current node.
|
||||||
|
MemoryNodesMap allocator_nodes_;
|
||||||
|
|
||||||
|
// This class is uncopyable and unassignable.
|
||||||
|
RawProcessMemoryNode(const RawProcessMemoryNode&) = delete;
|
||||||
|
RawProcessMemoryNode& operator=(const RawProcessMemoryNode&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace trace_processor
|
||||||
|
} // namespace perfetto
|
||||||
|
|
||||||
|
#endif // INCLUDE_PERFETTO_EXT_TRACE_PROCESSOR_IMPORTERS_MEMORY_TRACKER_RAW_PROCESS_MEMORY_NODE_H_
|
19
src/libtracing/perfetto/ext/trace_processor/rpc/BUILD.gn
Normal file
19
src/libtracing/perfetto/ext/trace_processor/rpc/BUILD.gn
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# 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("query_result_serializer") {
|
||||||
|
sources = [ "query_result_serializer.h" ]
|
||||||
|
}
|
|
@ -0,0 +1,102 @@
|
||||||
|
/*
|
||||||
|
* 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_TRACE_PROCESSOR_RPC_QUERY_RESULT_SERIALIZER_H_
|
||||||
|
#define INCLUDE_PERFETTO_EXT_TRACE_PROCESSOR_RPC_QUERY_RESULT_SERIALIZER_H_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace perfetto {
|
||||||
|
|
||||||
|
namespace protos {
|
||||||
|
namespace pbzero {
|
||||||
|
class QueryResult;
|
||||||
|
} // namespace pbzero
|
||||||
|
} // namespace protos
|
||||||
|
|
||||||
|
namespace trace_processor {
|
||||||
|
|
||||||
|
class Iterator;
|
||||||
|
class IteratorImpl;
|
||||||
|
|
||||||
|
// This class serializes a TraceProcessor query result (i.e. an Iterator)
|
||||||
|
// into batches of QueryResult (trace_processor.proto). This class
|
||||||
|
// returns results in batches, allowing to deal with O(M) results without
|
||||||
|
// full memory buffering. It works as follows:
|
||||||
|
// - The iterator is passed in the constructor.
|
||||||
|
// - The client is expected to call Serialize(out_buf) until EOF is reached.
|
||||||
|
// - For each Serialize() call, this class will serialize a batch of cells,
|
||||||
|
// stopping when either when a number of cells (|cells_per_batch_|) is reached
|
||||||
|
// or when the batch size exceeds (batch_split_threshold_).
|
||||||
|
// A batch is guaranteed to contain a number of cells that is an integer
|
||||||
|
// multiple of the column count (i.e. a batch is not truncated in the middle
|
||||||
|
// of a row).
|
||||||
|
// The intended use case is streaaming these batches onto through a
|
||||||
|
// chunked-encoded HTTP response, or through a repetition of Wasm calls.
|
||||||
|
class QueryResultSerializer {
|
||||||
|
public:
|
||||||
|
static constexpr uint32_t kDefaultBatchSplitThreshold = 128 * 1024;
|
||||||
|
explicit QueryResultSerializer(Iterator);
|
||||||
|
~QueryResultSerializer();
|
||||||
|
|
||||||
|
// No copy or move.
|
||||||
|
QueryResultSerializer(const QueryResultSerializer&) = delete;
|
||||||
|
QueryResultSerializer& operator=(const QueryResultSerializer&) = delete;
|
||||||
|
|
||||||
|
// Appends the data to the passed protozero message. It returns true if more
|
||||||
|
// chunks are available (i.e. it returns NOT(|eof_reached_||)). The caller is
|
||||||
|
// supposed to keep calling this function until it returns false.
|
||||||
|
bool Serialize(protos::pbzero::QueryResult*);
|
||||||
|
|
||||||
|
// Like the above but stitches everything together in a vector. Incurs in
|
||||||
|
// extra copies.
|
||||||
|
bool Serialize(std::vector<uint8_t>*);
|
||||||
|
|
||||||
|
void set_batch_size_for_testing(uint32_t cells_per_batch, uint32_t thres) {
|
||||||
|
cells_per_batch_ = cells_per_batch;
|
||||||
|
batch_split_threshold_ = thres;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void SerializeMetadata(protos::pbzero::QueryResult*);
|
||||||
|
void SerializeBatch(protos::pbzero::QueryResult*);
|
||||||
|
void MaybeSerializeError(protos::pbzero::QueryResult*);
|
||||||
|
|
||||||
|
std::unique_ptr<IteratorImpl> iter_;
|
||||||
|
const uint32_t num_cols_;
|
||||||
|
bool did_write_metadata_ = false;
|
||||||
|
bool eof_reached_ = false;
|
||||||
|
uint32_t col_ = UINT32_MAX;
|
||||||
|
|
||||||
|
// These params specify the thresholds for splitting the results in batches,
|
||||||
|
// in terms of: (1) max cells (row x cols); (2) serialized batch size in
|
||||||
|
// bytes, whichever is reached first. Note also that the byte limit is not
|
||||||
|
// 100% accurate and can occasionally yield to batches slighly larger than
|
||||||
|
// the limit (it splits on the next row *after* the limit is hit).
|
||||||
|
// Overridable for testing only.
|
||||||
|
uint32_t cells_per_batch_ = 50000;
|
||||||
|
uint32_t batch_split_threshold_ = kDefaultBatchSplitThreshold;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace trace_processor
|
||||||
|
} // namespace perfetto
|
||||||
|
|
||||||
|
#endif // INCLUDE_PERFETTO_EXT_TRACE_PROCESSOR_RPC_QUERY_RESULT_SERIALIZER_H_
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue