base: add ability to override platform-level functionality in base

In Google3, we need the ability to annotate the regions around blocking
syscalls so the userspace scheduler is aware of them. Because of this,
add functionality to mark such regions by having an abstraction of a
"platform" at a build system level and allow Bazel to override it.

Moreover, annotate read, write and poll syscalls in base with these
new functions: the "default" functions are empty noops.

Change-Id: I18c8246d32bac512494c2bc2cd5b02d840b82ec6
diff --git a/Android.bp b/Android.bp
index 2cea732..b5660da 100644
--- a/Android.bp
+++ b/Android.bp
@@ -18,6 +18,7 @@
 cc_binary {
     name: "heapprofd",
     srcs: [
+        ":perfetto_base_default_platform",
         ":perfetto_include_perfetto_base_base",
         ":perfetto_include_perfetto_ext_base_base",
         ":perfetto_include_perfetto_ext_base_version",
@@ -200,6 +201,7 @@
 cc_library_shared {
     name: "heapprofd_client",
     srcs: [
+        ":perfetto_base_default_platform",
         ":perfetto_include_perfetto_base_base",
         ":perfetto_include_perfetto_ext_base_base",
         ":perfetto_include_perfetto_public_abi_base",
@@ -232,6 +234,7 @@
 cc_library_shared {
     name: "heapprofd_client_api",
     srcs: [
+        ":perfetto_base_default_platform",
         ":perfetto_include_perfetto_base_base",
         ":perfetto_include_perfetto_ext_base_base",
         ":perfetto_include_perfetto_public_abi_base",
@@ -280,6 +283,7 @@
 cc_library_shared {
     name: "heapprofd_standalone_client",
     srcs: [
+        ":perfetto_base_default_platform",
         ":perfetto_include_perfetto_base_base",
         ":perfetto_include_perfetto_ext_base_base",
         ":perfetto_include_perfetto_ext_base_version",
@@ -455,6 +459,7 @@
 cc_binary_host {
     name: "ipc_plugin",
     srcs: [
+        ":perfetto_base_default_platform",
         ":perfetto_include_perfetto_base_base",
         ":perfetto_include_perfetto_ext_base_base",
         ":perfetto_include_perfetto_public_abi_base",
@@ -478,6 +483,7 @@
 cc_library_shared {
     name: "libperfetto",
     srcs: [
+        ":perfetto_base_default_platform",
         ":perfetto_include_perfetto_base_base",
         ":perfetto_include_perfetto_ext_base_base",
         ":perfetto_include_perfetto_ext_base_version",
@@ -704,6 +710,7 @@
 cc_library_static {
     name: "libperfetto_client_experimental",
     srcs: [
+        ":perfetto_base_default_platform",
         ":perfetto_include_perfetto_base_base",
         ":perfetto_include_perfetto_ext_base_base",
         ":perfetto_include_perfetto_ext_base_version",
@@ -921,6 +928,7 @@
 cc_binary {
     name: "perfetto",
     srcs: [
+        ":perfetto_base_default_platform",
         ":perfetto_include_perfetto_base_base",
         ":perfetto_include_perfetto_ext_base_base",
         ":perfetto_include_perfetto_ext_base_version",
@@ -1070,10 +1078,19 @@
     ],
 }
 
+// GN: //src/base:perfetto_base_default_platform
+filegroup {
+    name: "perfetto_base_default_platform",
+    srcs: [
+        "src/base/default_platform.cc",
+    ],
+}
+
 // GN: //test/cts:perfetto_cts_deps
 cc_library_static {
     name: "perfetto_cts_deps",
     srcs: [
+        ":perfetto_base_default_platform",
         ":perfetto_include_perfetto_base_base",
         ":perfetto_include_perfetto_ext_base_base",
         ":perfetto_include_perfetto_ext_base_version",
@@ -1364,6 +1381,7 @@
 cc_library_static {
     name: "perfetto_cts_jni_deps",
     srcs: [
+        ":perfetto_base_default_platform",
         ":perfetto_include_perfetto_base_base",
         ":perfetto_include_perfetto_ext_base_base",
         ":perfetto_include_perfetto_ext_base_version",
@@ -1800,6 +1818,7 @@
 cc_test {
     name: "perfetto_integrationtests",
     srcs: [
+        ":perfetto_base_default_platform",
         ":perfetto_include_perfetto_base_base",
         ":perfetto_include_perfetto_ext_base_base",
         ":perfetto_include_perfetto_ext_base_version",
@@ -8784,6 +8803,7 @@
 cc_binary_host {
     name: "perfetto_src_protozero_protoc_plugin_cppgen_plugin",
     srcs: [
+        ":perfetto_base_default_platform",
         ":perfetto_include_perfetto_base_base",
         ":perfetto_include_perfetto_ext_base_base",
         ":perfetto_include_perfetto_public_abi_base",
@@ -11258,6 +11278,7 @@
 cc_test {
     name: "perfetto_unittests",
     srcs: [
+        ":perfetto_base_default_platform",
         ":perfetto_include_perfetto_base_base",
         ":perfetto_include_perfetto_ext_base_base",
         ":perfetto_include_perfetto_ext_base_http_http",
@@ -11749,6 +11770,7 @@
 cc_library_static {
     name: "perfetto_vts_deps",
     srcs: [
+        ":perfetto_base_default_platform",
         ":perfetto_include_perfetto_base_base",
         ":perfetto_include_perfetto_ext_base_base",
         ":perfetto_include_perfetto_ext_base_version",
@@ -12031,6 +12053,7 @@
 cc_binary_host {
     name: "protozero_plugin",
     srcs: [
+        ":perfetto_base_default_platform",
         ":perfetto_include_perfetto_base_base",
         ":perfetto_include_perfetto_ext_base_base",
         ":perfetto_include_perfetto_public_abi_base",
@@ -12054,6 +12077,7 @@
 cc_binary {
     name: "trace_processor_shell",
     srcs: [
+        ":perfetto_base_default_platform",
         ":perfetto_include_perfetto_base_base",
         ":perfetto_include_perfetto_ext_base_base",
         ":perfetto_include_perfetto_ext_base_http_http",
@@ -12268,6 +12292,7 @@
 cc_binary_host {
     name: "traceconv",
     srcs: [
+        ":perfetto_base_default_platform",
         ":perfetto_include_perfetto_base_base",
         ":perfetto_include_perfetto_ext_base_base",
         ":perfetto_include_perfetto_ext_base_version",
@@ -12468,6 +12493,7 @@
 cc_binary {
     name: "traced_perf",
     srcs: [
+        ":perfetto_base_default_platform",
         ":perfetto_include_perfetto_base_base",
         ":perfetto_include_perfetto_ext_base_base",
         ":perfetto_include_perfetto_ext_base_version",
@@ -12662,6 +12688,7 @@
 cc_binary {
     name: "trigger_perfetto",
     srcs: [
+        ":perfetto_base_default_platform",
         ":perfetto_include_perfetto_base_base",
         ":perfetto_include_perfetto_ext_base_base",
         ":perfetto_include_perfetto_ext_base_version",
diff --git a/BUILD b/BUILD
index 5173afc..6f77ebc 100644
--- a/BUILD
+++ b/BUILD
@@ -49,6 +49,21 @@
 # Internal targets
 # ##############################################################################
 
+# GN target: //src/base:perfetto_base_default_platform
+perfetto_cc_library(
+    name = "perfetto_base_default_platform",
+    srcs = [
+        "src/base/default_platform.cc",
+    ],
+    hdrs = [
+        ":include_perfetto_base_base",
+        ":include_perfetto_ext_base_base",
+        ":include_perfetto_public_abi_base",
+        ":include_perfetto_public_base",
+    ],
+    linkstatic = True,
+)
+
 # GN target: //src/ipc/protoc_plugin:ipc_plugin
 perfetto_cc_binary(
     name = "ipc_plugin",
@@ -392,6 +407,7 @@
         "include/perfetto/ext/base/paged_memory.h",
         "include/perfetto/ext/base/periodic_task.h",
         "include/perfetto/ext/base/pipe.h",
+        "include/perfetto/ext/base/platform.h",
         "include/perfetto/ext/base/scoped_file.h",
         "include/perfetto/ext/base/small_set.h",
         "include/perfetto/ext/base/small_vector.h",
@@ -768,6 +784,8 @@
         ":include_perfetto_public_abi_base",
         ":include_perfetto_public_base",
     ],
+    deps = [
+    ] + PERFETTO_CONFIG.deps.base_platform,
     linkstatic = True,
 )
 
diff --git a/bazel/standalone/perfetto_cfg.bzl b/bazel/standalone/perfetto_cfg.bzl
index ce2764e..e58b5aa 100644
--- a/bazel/standalone/perfetto_cfg.bzl
+++ b/bazel/standalone/perfetto_cfg.bzl
@@ -40,6 +40,10 @@
         # internal builds.
         version_header = ["//:cc_perfetto_version_header"],
 
+        # Target exposing platform-specific functionality for base. This is
+        # overriden in Google internal builds.
+        base_platform = ["//:perfetto_base_default_platform"],
+
         zlib = ["@perfetto_dep_zlib//:zlib"],
         jsoncpp = ["@perfetto_dep_jsoncpp//:jsoncpp"],
         linenoise = ["@perfetto_dep_linenoise//:linenoise"],
diff --git a/gn/BUILD.gn b/gn/BUILD.gn
index 4be2820..0130ce7 100644
--- a/gn/BUILD.gn
+++ b/gn/BUILD.gn
@@ -405,6 +405,13 @@
   }
 }
 
+# Allows overriding platform-specific functionality used by base at a
+# build-system level. This allows e.g. different implementations of base
+# functions in Google3.
+group("base_platform") {
+  public_deps = [ "../src/base:perfetto_base_default_platform" ]
+}
+
 # Used by fuzzers.
 if (enable_perfetto_fuzzers && use_libfuzzer) {
   group("libfuzzer") {
diff --git a/include/perfetto/ext/base/BUILD.gn b/include/perfetto/ext/base/BUILD.gn
index 3553d8c..5a44be4 100644
--- a/include/perfetto/ext/base/BUILD.gn
+++ b/include/perfetto/ext/base/BUILD.gn
@@ -36,6 +36,7 @@
     "paged_memory.h",
     "periodic_task.h",
     "pipe.h",
+    "platform.h",
     "scoped_file.h",
     "small_set.h",
     "small_vector.h",
diff --git a/include/perfetto/ext/base/platform.h b/include/perfetto/ext/base/platform.h
new file mode 100644
index 0000000..352e84d
--- /dev/null
+++ b/include/perfetto/ext/base/platform.h
@@ -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_
diff --git a/src/base/BUILD.gn b/src/base/BUILD.gn
index b53b08a..6a58f0b 100644
--- a/src/base/BUILD.gn
+++ b/src/base/BUILD.gn
@@ -25,7 +25,10 @@
     is_linux || is_chromeos || is_android || is_mac || is_win
 
 perfetto_component("base") {
-  deps = [ "../../gn:default_deps" ]
+  deps = [
+    "../../gn:base_platform",
+    "../../gn:default_deps",
+  ]
   public_deps = [
     "../../include/perfetto/base",
     "../../include/perfetto/ext/base",
@@ -79,6 +82,16 @@
   }
 }
 
+# This target needs to be named as such because it's exposed directly in Bazel
+# and Android.bp.
+perfetto_component("perfetto_base_default_platform") {
+  deps = [
+    "../../gn:default_deps",
+    "../../include/perfetto/ext/base",
+  ]
+  sources = [ "default_platform.cc" ]
+}
+
 perfetto_component("version") {
   deps = [
     ":base",
diff --git a/src/base/default_platform.cc b/src/base/default_platform.cc
new file mode 100644
index 0000000..acc3cf3
--- /dev/null
+++ b/src/base/default_platform.cc
@@ -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.
+ */
+
+#include "perfetto/ext/base/platform.h"
+
+namespace perfetto {
+namespace base {
+namespace platform {
+
+// This is a no-op outside of Google3 where we have some custom logic to deal
+// with the userspace scheduler.
+void BeforeMaybeBlockingSyscall() {}
+
+// This is a no-op outside of Google3 where we have some custom logic to deal
+// with the userspace scheduler.
+void AfterMaybeBlockingSyscall() {}
+
+}  // namespace platform
+}  // namespace base
+}  // namespace perfetto
diff --git a/src/base/file_utils.cc b/src/base/file_utils.cc
index 91a63bd..66ab5f6 100644
--- a/src/base/file_utils.cc
+++ b/src/base/file_utils.cc
@@ -29,6 +29,7 @@
 #include "perfetto/base/platform_handle.h"
 #include "perfetto/base/status.h"
 #include "perfetto/ext/base/optional.h"
+#include "perfetto/ext/base/platform.h"
 #include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/ext/base/utils.h"
 
@@ -77,11 +78,15 @@
 }  // namespace
 
 ssize_t Read(int fd, void* dst, size_t dst_size) {
+  ssize_t ret;
+  platform::BeforeMaybeBlockingSyscall();
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  return _read(fd, dst, static_cast<unsigned>(dst_size));
+  ret = _read(fd, dst, static_cast<unsigned>(dst_size));
 #else
-  return PERFETTO_EINTR(read(fd, dst, dst_size));
+  ret = PERFETTO_EINTR(read(fd, dst, dst_size));
 #endif
+  platform::AfterMaybeBlockingSyscall();
+  return ret;
 }
 
 bool ReadFileDescriptor(int fd, std::string* out) {
@@ -156,8 +161,10 @@
     // write() on windows takes an unsigned int size.
     uint32_t bytes_left = static_cast<uint32_t>(
         std::min(count - written, static_cast<size_t>(UINT32_MAX)));
+    platform::BeforeMaybeBlockingSyscall();
     ssize_t wr = PERFETTO_EINTR(
         write(fd, static_cast<const char*>(buf) + written, bytes_left));
+    platform::AfterMaybeBlockingSyscall();
     if (wr == 0)
       break;
     if (wr < 0)
diff --git a/src/base/unix_task_runner.cc b/src/base/unix_task_runner.cc
index f6b6c19..9fc8f3c 100644
--- a/src/base/unix_task_runner.cc
+++ b/src/base/unix_task_runner.cc
@@ -16,6 +16,7 @@
 
 #include "perfetto/base/build_config.h"
 
+#include "perfetto/ext/base/platform.h"
 #include "perfetto/ext/base/unix_task_runner.h"
 
 #include <errno.h>
@@ -77,8 +78,10 @@
     // returned.
     PostFileDescriptorWatches(ret);
 #else
+    platform::BeforeMaybeBlockingSyscall();
     int ret = PERFETTO_EINTR(poll(
         &poll_fds_[0], static_cast<nfds_t>(poll_fds_.size()), poll_timeout_ms));
+    platform::AfterMaybeBlockingSyscall();
     PERFETTO_CHECK(ret >= 0);
     PostFileDescriptorWatches(0 /*ignored*/);
 #endif
diff --git a/src/base/watchdog_posix.cc b/src/base/watchdog_posix.cc
index b92e9ce..fcbf713 100644
--- a/src/base/watchdog_posix.cc
+++ b/src/base/watchdog_posix.cc
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include "perfetto/ext/base/platform.h"
 #include "perfetto/ext/base/watchdog.h"
 
 #if PERFETTO_BUILDFLAG(PERFETTO_WATCHDOG)
@@ -231,7 +232,9 @@
     // We use the poll() timeout to drive the periodic ticks for the cpu/memory
     // checks. The only other case when the poll() unblocks is when we crash
     // (or have to quit via enabled_ == false, but that happens only in tests).
+    platform::BeforeMaybeBlockingSyscall();
     auto ret = poll(fds, kFdCount, static_cast<int>(polling_interval_ms_));
+    platform::AfterMaybeBlockingSyscall();
     if (!enabled_)
       return;
     if (ret < 0) {
diff --git a/tools/gen_android_bp b/tools/gen_android_bp
index bb83471..48a3e08 100755
--- a/tools/gen_android_bp
+++ b/tools/gen_android_bp
@@ -58,6 +58,7 @@
     '//:perfetto_unittests',
     '//protos/perfetto/trace:perfetto_trace_protos',
     '//src/android_internal:libperfetto_android_internal',
+    '//src/base:perfetto_base_default_platform',
     '//src/perfetto_cmd:perfetto',
     '//src/perfetto_cmd:trigger_perfetto',
     '//src/profiling/memory:heapprofd_client',
@@ -262,6 +263,10 @@
 }
 
 
+def enable_base_platform(module):
+  module.srcs.add(':perfetto_base_default_platform')
+
+
 def enable_gtest_and_gmock(module):
   module.static_libs.add('libgmock')
   module.static_libs.add('libgtest')
@@ -357,6 +362,8 @@
         lambda x: None,
     '//gn:protoc':
         lambda x: None,
+    '//gn:base_platform':
+        enable_base_platform,
     '//gn:gtest_and_gmock':
         enable_gtest_and_gmock,
     '//gn:libunwind':
diff --git a/tools/gen_bazel b/tools/gen_bazel
index 9c5b0c7..10b047d 100755
--- a/tools/gen_bazel
+++ b/tools/gen_bazel
@@ -70,6 +70,7 @@
 # These targets are required by internal build rules but don't need to be
 # exported publicly.
 default_targets = [
+    '//src/base:perfetto_base_default_platform',
     '//src/ipc:perfetto_ipc',
     '//src/ipc/protoc_plugin:ipc_plugin',
     '//src/protozero:protozero',
@@ -102,6 +103,7 @@
 # depends on.
 external_deps = {
     '//gn:default_deps': [],
+    '//gn:base_platform': ['PERFETTO_CONFIG.deps.base_platform'],
     '//gn:jsoncpp': ['PERFETTO_CONFIG.deps.jsoncpp'],
     '//gn:linenoise': ['PERFETTO_CONFIG.deps.linenoise'],
     '//gn:protobuf_full': ['PERFETTO_CONFIG.deps.protobuf_full'],