Add IPC layer + Platform implementation for Windows
The final CL that adds the Windows-specific IPC bits.
This implements the IPC transport as a AF_UNIX socket
and a named shared memory region. AF_UNIX doesn't
bring any major benefits really and I wonder whether
we should just use a TCP socket. In fact, AF_UNIX on
Windows misses the interesting bits of UNIX sockets:
(1) FD-passing is not supported; (2) Peer credentials
are not supported. Given that the shared memory is
based on an unguessable string, we could send that
over TCP as well.
Bug: 174454879
Test: manual on Windows, the following works:
perfetto_unittests.exe
perfetto_integrationtests.exe
traced.exe + perfetto.exe
stress_test.exe (there is something odd scheduling-wise here but
seems unrelated with the IPC port)
Change-Id: I77cb42940d8bd2ffbee2454ec8d6982781a3096b
diff --git a/Android.bp b/Android.bp
index 2bdae45..c0add08 100644
--- a/Android.bp
+++ b/Android.bp
@@ -8384,6 +8384,7 @@
"src/tracing/ipc/default_socket.cc",
"src/tracing/ipc/memfd.cc",
"src/tracing/ipc/posix_shared_memory.cc",
+ "src/tracing/ipc/shared_memory_windows.cc",
],
}
@@ -8426,6 +8427,7 @@
name: "perfetto_src_tracing_platform_impl",
srcs: [
"src/tracing/platform_posix.cc",
+ "src/tracing/platform_windows.cc",
],
}
diff --git a/BUILD b/BUILD
index 35e71df..7c861f3 100644
--- a/BUILD
+++ b/BUILD
@@ -1582,6 +1582,8 @@
"src/tracing/ipc/memfd.h",
"src/tracing/ipc/posix_shared_memory.cc",
"src/tracing/ipc/posix_shared_memory.h",
+ "src/tracing/ipc/shared_memory_windows.cc",
+ "src/tracing/ipc/shared_memory_windows.h",
],
)
@@ -1634,6 +1636,7 @@
name = "src_tracing_platform_impl",
srcs = [
"src/tracing/platform_posix.cc",
+ "src/tracing/platform_windows.cc",
],
)
diff --git a/gn/perfetto.gni b/gn/perfetto.gni
index 13831a0..a1c5ff0 100644
--- a/gn/perfetto.gni
+++ b/gn/perfetto.gni
@@ -144,9 +144,12 @@
# system backend in the client library.
# This includes building things that rely on POSIX sockets, this places
# limitations on the supported operating systems.
- enable_perfetto_ipc = !is_win && !is_fuchsia && !is_nacl &&
- (perfetto_build_standalone ||
- perfetto_build_with_android || build_with_chromium)
+ # For now the IPC layer is conservatively not enabled on Chromium+Windows
+ # builds.
+ enable_perfetto_ipc =
+ !is_fuchsia && !is_nacl &&
+ (perfetto_build_standalone || perfetto_build_with_android ||
+ (build_with_chromium && !is_win))
# Makes the heap profiling daemon target reachable. It works only on Android,
# but is built on Linux as well for test/compiler coverage.
@@ -186,7 +189,7 @@
build_with_chromium || perfetto_build_with_android
enable_perfetto_integration_tests =
- (perfetto_build_standalone && !is_win) || perfetto_build_with_android
+ perfetto_build_standalone || perfetto_build_with_android
enable_perfetto_benchmarks = perfetto_build_standalone && !is_win
@@ -310,9 +313,6 @@
# |is_perfetto_build_generator| must be true.
assert(!perfetto_build_with_android || is_perfetto_build_generator)
-# The IPC layer based on UNIX sockets can't be built on Win.
-assert(!enable_perfetto_ipc || !is_win)
-
# We should never end up in a state where is_perfetto_embedder=true but
# perfetto_build_with_embedder=false.
assert(!is_perfetto_embedder || perfetto_build_with_embedder)
diff --git a/gn/proto_library.gni b/gn/proto_library.gni
index 03d6455..5b87753 100644
--- a/gn/proto_library.gni
+++ b/gn/proto_library.gni
@@ -173,6 +173,17 @@
} else {
deps += [ "$perfetto_root_path/src/ipc:common" ]
}
+ if (is_win) {
+ # TODO(primiano): investigate this. In Windows standalone builds, some
+ # executable targets end up in a state where no code pulls a dep on the
+ # ipc:client (in turn that seems a subtle consequence of not having
+ # traced_probes on Windows). This dep here is effectively needed because
+ # the client-side code in the generated .ipc.cc effectively depends on the
+ # client-side IPC library. Perhaps we just should do this unconditionally
+ # on all platforms?
+ deps += [ "$perfetto_root_path/src/ipc:client" ]
+ }
+
if (defined(invoker.deps)) {
deps += invoker.deps
}
diff --git a/gn/standalone/BUILD.gn b/gn/standalone/BUILD.gn
index af70070..31941ed 100644
--- a/gn/standalone/BUILD.gn
+++ b/gn/standalone/BUILD.gn
@@ -161,6 +161,8 @@
cflags += [
"/bigobj", # Some of our files are bigger than the regular limits.
"/Gy", # Enable function-level linking.
+ "/FS", # Preserve previous PDB behavior.
+ "/utf-8", # Assume UTF-8 by default to avoid code page dependencies.
]
defines += [
"_CRT_NONSTDC_NO_WARNINGS",
diff --git a/protos/perfetto/ipc/producer_port.proto b/protos/perfetto/ipc/producer_port.proto
index 1b193eb..ac1b015 100644
--- a/protos/perfetto/ipc/producer_port.proto
+++ b/protos/perfetto/ipc/producer_port.proto
@@ -152,6 +152,11 @@
// SetupTracing response. See TracingService::ConnectProducer() and
// |using_shmem_provided_by_producer| in InitializeConnectionResponse.
optional bool producer_provided_shmem = 6;
+
+ // On Windows, when producer_provided_shmem = true, the client creates a named
+ // SHM region and passes the name (an unguessable token) back to the service.
+ // Introduced in v13.
+ optional string shm_key_windows = 7;
}
message InitializeConnectionResponse {
@@ -262,9 +267,16 @@
message StopDataSource { optional uint64 instance_id = 1; }
- // This message also transports the file descriptor for the shared memory
- // buffer (not a proto field).
- message SetupTracing { optional uint32 shared_buffer_page_size_kb = 1; }
+ // On Android/Linux/Mac this message also transports the file descriptor for
+ // the shared memory buffer (not a proto field).
+ message SetupTracing {
+ optional uint32 shared_buffer_page_size_kb = 1;
+
+ // On Windows, instead, we pass the name (an unguessable token) of a shared
+ // memory region that can be attached by the other process by name.
+ // Introduced in v13.
+ optional string shm_key_windows = 2;
+ }
message Flush {
// The instance id (i.e. StartDataSource.new_instance_id) of the data
diff --git a/src/tracing/BUILD.gn b/src/tracing/BUILD.gn
index 43ce774..fcfddbf 100644
--- a/src/tracing/BUILD.gn
+++ b/src/tracing/BUILD.gn
@@ -50,14 +50,17 @@
}
}
-# Separate target because the embedder might not want this (e.g. on Windows).
+# Separate target because the embedder might not want this.
source_set("platform_impl") {
deps = [
"../../gn:default_deps",
"../../include/perfetto/tracing",
"../base",
]
- sources = [ "platform_posix.cc" ]
+ sources = [
+ "platform_posix.cc",
+ "platform_windows.cc",
+ ]
}
# Fake platform that allows buiding the client lib on all OSes. You can only use
diff --git a/src/tracing/ipc/BUILD.gn b/src/tracing/ipc/BUILD.gn
index 93c97a6..f86aeb5 100644
--- a/src/tracing/ipc/BUILD.gn
+++ b/src/tracing/ipc/BUILD.gn
@@ -32,6 +32,8 @@
"memfd.h",
"posix_shared_memory.cc",
"posix_shared_memory.h",
+ "shared_memory_windows.cc",
+ "shared_memory_windows.h",
]
deps = [
"../../../gn:default_deps",
diff --git a/src/tracing/ipc/default_socket.cc b/src/tracing/ipc/default_socket.cc
index 3ad5cee..7349f92 100644
--- a/src/tracing/ipc/default_socket.cc
+++ b/src/tracing/ipc/default_socket.cc
@@ -23,7 +23,12 @@
#include "perfetto/ext/tracing/core/basic_types.h"
#include <stdlib.h>
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
#include <unistd.h>
+#endif
namespace perfetto {
#if !PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
diff --git a/src/tracing/ipc/producer/producer_ipc_client_impl.cc b/src/tracing/ipc/producer/producer_ipc_client_impl.cc
index 64b1023..694a3a9 100644
--- a/src/tracing/ipc/producer/producer_ipc_client_impl.cc
+++ b/src/tracing/ipc/producer/producer_ipc_client_impl.cc
@@ -29,7 +29,12 @@
#include "perfetto/tracing/core/data_source_config.h"
#include "perfetto/tracing/core/data_source_descriptor.h"
#include "perfetto/tracing/core/trace_config.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include "src/tracing/ipc/shared_memory_windows.h"
+#else
#include "src/tracing/ipc/posix_shared_memory.h"
+#endif
// TODO(fmayer): think to what happens when ProducerIPCClientImpl gets destroyed
// w.r.t. the Producer pointer. Also think to lifetime of the Producer* during
@@ -158,8 +163,13 @@
int shm_fd = -1;
if (shared_memory_) {
- shm_fd = static_cast<PosixSharedMemory*>(shared_memory_.get())->fd();
req.set_producer_provided_shmem(true);
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ auto key = static_cast<SharedMemoryWindows*>(shared_memory_.get())->key();
+ req.set_shm_key_windows(key);
+#else
+ shm_fd = static_cast<PosixSharedMemory*>(shared_memory_.get())->fd();
+#endif
}
#if PERFETTO_DCHECK_IS_ON()
@@ -253,15 +263,25 @@
}
if (cmd.has_setup_tracing()) {
+ std::unique_ptr<SharedMemory> ipc_shared_memory;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ const std::string& shm_key = cmd.setup_tracing().shm_key_windows();
+ if (!shm_key.empty())
+ ipc_shared_memory = SharedMemoryWindows::Attach(shm_key);
+#else
base::ScopedFile shmem_fd = ipc_channel_->TakeReceivedFD();
if (shmem_fd) {
+ // TODO(primiano): handle mmap failure in case of OOM.
+ ipc_shared_memory =
+ PosixSharedMemory::AttachToFd(std::move(shmem_fd),
+ /*require_seals_if_supported=*/false);
+ }
+#endif
+ if (ipc_shared_memory) {
// This is the nominal case used in most configurations, where the service
// provides the SMB.
PERFETTO_CHECK(!is_shmem_provided_by_producer_ && !shared_memory_);
- // TODO(primiano): handle mmap failure in case of OOM.
- shared_memory_ =
- PosixSharedMemory::AttachToFd(std::move(shmem_fd),
- /*require_seals_if_supported=*/false);
+ shared_memory_ = std::move(ipc_shared_memory);
shared_buffer_page_size_kb_ =
cmd.setup_tracing().shared_buffer_page_size_kb();
shared_memory_arbiter_ = SharedMemoryArbiter::CreateInstance(
diff --git a/src/tracing/ipc/service/producer_ipc_service.cc b/src/tracing/ipc/service/producer_ipc_service.cc
index cfa79b1..dc74374 100644
--- a/src/tracing/ipc/service/producer_ipc_service.cc
+++ b/src/tracing/ipc/service/producer_ipc_service.cc
@@ -26,7 +26,12 @@
#include "perfetto/ext/tracing/core/tracing_service.h"
#include "perfetto/tracing/core/data_source_config.h"
#include "perfetto/tracing/core/data_source_descriptor.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include "src/tracing/ipc/shared_memory_windows.h"
+#else
#include "src/tracing/ipc/posix_shared_memory.h"
+#endif
// The remote Producer(s) are not trusted. All the methods from the ProducerPort
// IPC layer (e.g. RegisterDataSource()) must assume that the remote Producer is
@@ -93,7 +98,18 @@
// If the producer provided an SMB, tell the service to attempt to adopt it.
std::unique_ptr<SharedMemory> shmem;
if (req.producer_provided_shmem()) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ if (!req.has_shm_key_windows() || req.shm_key_windows().empty()) {
+ PERFETTO_ELOG(
+ "shm_key_windows must be non-empty when "
+ "producer_provided_shmem = true");
+ } else {
+ shmem = SharedMemoryWindows::Attach(req.shm_key_windows());
+ // Attach() does error logging if something fails, no need to extra ELOGs.
+ }
+#else
base::ScopedFile shmem_fd = ipc::Service::TakeReceivedFD();
+
if (shmem_fd) {
shmem = PosixSharedMemory::AttachToFd(
std::move(shmem_fd), /*require_seals_if_supported=*/true);
@@ -107,6 +123,7 @@
"InitializeConnectionRequest's producer_provided_shmem flag is set "
"but the producer didn't provide an FD");
}
+#endif
}
// ConnectProducer will call OnConnect() on the next task.
@@ -455,10 +472,17 @@
// Nominal case (% Chrome): service provides SMB.
setup_tracing->set_shared_buffer_page_size_kb(
static_cast<uint32_t>(service_endpoint->shared_buffer_page_size_kb()));
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ const std::string& shm_key =
+ static_cast<SharedMemoryWindows*>(service_endpoint->shared_memory())
+ ->key();
+ setup_tracing->set_shm_key_windows(shm_key);
+#else
const int shm_fd =
static_cast<PosixSharedMemory*>(service_endpoint->shared_memory())
->fd();
cmd.set_fd(shm_fd);
+#endif
}
async_producer_commands.Resolve(std::move(cmd));
}
diff --git a/src/tracing/ipc/service/service_ipc_host_impl.cc b/src/tracing/ipc/service/service_ipc_host_impl.cc
index d19fef6..5618b3c 100644
--- a/src/tracing/ipc/service/service_ipc_host_impl.cc
+++ b/src/tracing/ipc/service/service_ipc_host_impl.cc
@@ -20,10 +20,15 @@
#include "perfetto/base/task_runner.h"
#include "perfetto/ext/ipc/host.h"
#include "perfetto/ext/tracing/core/tracing_service.h"
-#include "src/tracing/ipc/posix_shared_memory.h"
#include "src/tracing/ipc/service/consumer_ipc_service.h"
#include "src/tracing/ipc/service/producer_ipc_service.h"
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include "src/tracing/ipc/shared_memory_windows.h"
+#else
+#include "src/tracing/ipc/posix_shared_memory.h"
+#endif
+
namespace perfetto {
// TODO(fmayer): implement per-uid connection limit (b/69093705).
@@ -66,8 +71,13 @@
bool ServiceIPCHostImpl::DoStart() {
// Create and initialize the platform-independent tracing business logic.
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ std::unique_ptr<SharedMemory::Factory> shm_factory(
+ new SharedMemoryWindows::Factory());
+#else
std::unique_ptr<SharedMemory::Factory> shm_factory(
new PosixSharedMemory::Factory());
+#endif
svc_ = TracingService::CreateInstance(std::move(shm_factory), task_runner_);
if (!producer_ipc_port_ || !consumer_ipc_port_) {
diff --git a/src/tracing/ipc/shared_memory_windows.cc b/src/tracing/ipc/shared_memory_windows.cc
new file mode 100644
index 0000000..579d810
--- /dev/null
+++ b/src/tracing/ipc/shared_memory_windows.cc
@@ -0,0 +1,113 @@
+/*
+ * 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.
+ */
+
+#include "src/tracing/ipc/shared_memory_windows.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+
+#include <memory>
+#include <random>
+
+#include <Windows.h>
+
+#include "perfetto/base/logging.h"
+#include "perfetto/ext/base/string_utils.h"
+
+namespace perfetto {
+
+// static
+std::unique_ptr<SharedMemoryWindows> SharedMemoryWindows::Create(size_t size) {
+ base::ScopedPlatformHandle shmem_handle;
+ std::random_device rnd_dev;
+ uint64_t rnd_key = (static_cast<uint64_t>(rnd_dev()) << 32) | rnd_dev();
+ std::string key = "perfetto_shm_" + base::Uint64ToHexStringNoPrefix(rnd_key);
+ shmem_handle.reset(CreateFileMappingA(
+ INVALID_HANDLE_VALUE, // Use paging file.
+ nullptr, // Default security.
+ PAGE_READWRITE,
+ static_cast<DWORD>(size >> 32), // maximum object size (high-order DWORD)
+ static_cast<DWORD>(size), // maximum object size (low-order DWORD)
+ key.c_str()));
+
+ if (!shmem_handle) {
+ PERFETTO_PLOG("CreateFileMapping() call failed");
+ return nullptr;
+ }
+ void* start =
+ MapViewOfFile(*shmem_handle, FILE_MAP_ALL_ACCESS, /*offsetHigh=*/0,
+ /*offsetLow=*/0, size);
+ if (!start) {
+ PERFETTO_PLOG("MapViewOfFile() failed");
+ return nullptr;
+ }
+
+ return std::unique_ptr<SharedMemoryWindows>(new SharedMemoryWindows(
+ start, size, std::move(key), std::move(shmem_handle)));
+}
+
+// static
+std::unique_ptr<SharedMemoryWindows> SharedMemoryWindows::Attach(
+ const std::string& key) {
+ base::ScopedPlatformHandle shmem_handle;
+ shmem_handle.reset(
+ OpenFileMappingA(FILE_MAP_ALL_ACCESS, /*inherit=*/false, key.c_str()));
+ if (!shmem_handle) {
+ PERFETTO_PLOG("Failed to OpenFileMapping()");
+ return nullptr;
+ }
+
+ void* start =
+ MapViewOfFile(*shmem_handle, FILE_MAP_ALL_ACCESS, /*offsetHigh=*/0,
+ /*offsetLow=*/0, /*dwNumberOfBytesToMap=*/0);
+ if (!start) {
+ PERFETTO_PLOG("MapViewOfFile() failed");
+ return nullptr;
+ }
+
+ MEMORY_BASIC_INFORMATION info{};
+ if (!VirtualQuery(start, &info, sizeof(info))) {
+ PERFETTO_PLOG("VirtualQuery() failed");
+ return nullptr;
+ }
+ size_t size = info.RegionSize;
+ return std::unique_ptr<SharedMemoryWindows>(
+ new SharedMemoryWindows(start, size, key, std::move(shmem_handle)));
+}
+
+SharedMemoryWindows::SharedMemoryWindows(void* start,
+ size_t size,
+ std::string key,
+ base::ScopedPlatformHandle handle)
+ : start_(start),
+ size_(size),
+ key_(std::move(key)),
+ handle_(std::move(handle)) {}
+
+SharedMemoryWindows::~SharedMemoryWindows() {
+ if (start_)
+ UnmapViewOfFile(start_);
+}
+
+SharedMemoryWindows::Factory::~Factory() = default;
+
+std::unique_ptr<SharedMemory> SharedMemoryWindows::Factory::CreateSharedMemory(
+ size_t size) {
+ return SharedMemoryWindows::Create(size);
+}
+
+} // namespace perfetto
+
+#endif // !OS_WIN
diff --git a/src/tracing/ipc/shared_memory_windows.h b/src/tracing/ipc/shared_memory_windows.h
new file mode 100644
index 0000000..803c8a8
--- /dev/null
+++ b/src/tracing/ipc/shared_memory_windows.h
@@ -0,0 +1,69 @@
+/*
+ * 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 SRC_TRACING_IPC_SHARED_MEMORY_WINDOWS_H_
+#define SRC_TRACING_IPC_SHARED_MEMORY_WINDOWS_H_
+
+#include "perfetto/base/build_config.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+
+#include "perfetto/base/build_config.h"
+#include "perfetto/ext/base/scoped_file.h"
+#include "perfetto/ext/tracing/core/shared_memory.h"
+
+namespace perfetto {
+
+// Implements the SharedMemory and its factory for the Windows IPC transport.
+// This used only for standalone builds and NOT in chromium, which instead uses
+// a custom Mojo wrapper (MojoSharedMemory in chromium's //services/tracing/).
+class SharedMemoryWindows : public SharedMemory {
+ public:
+ class Factory : public SharedMemory::Factory {
+ public:
+ ~Factory() override;
+ std::unique_ptr<SharedMemory> CreateSharedMemory(size_t) override;
+ };
+
+ // Create a brand new SHM region.
+ static std::unique_ptr<SharedMemoryWindows> Create(size_t size);
+ static std::unique_ptr<SharedMemoryWindows> Attach(const std::string& key);
+ ~SharedMemoryWindows() override;
+ const std::string& key() const { return key_; }
+
+ // SharedMemory implementation.
+ void* start() const override { return start_; }
+ size_t size() const override { return size_; }
+
+ private:
+ SharedMemoryWindows(void* start,
+ size_t size,
+ std::string,
+ base::ScopedPlatformHandle);
+ SharedMemoryWindows(const SharedMemoryWindows&) = delete;
+ SharedMemoryWindows& operator=(const SharedMemoryWindows&) = delete;
+
+ void* const start_;
+ const size_t size_;
+ std::string key_;
+ base::ScopedPlatformHandle handle_;
+};
+
+} // namespace perfetto
+
+#endif // OS_WIN
+
+#endif // SRC_TRACING_IPC_SHARED_MEMORY_WINDOWS_H_
diff --git a/src/tracing/platform_windows.cc b/src/tracing/platform_windows.cc
new file mode 100644
index 0000000..e442c65
--- /dev/null
+++ b/src/tracing/platform_windows.cc
@@ -0,0 +1,166 @@
+/*
+ * 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.
+ */
+
+#include "perfetto/base/build_config.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+
+#include <Windows.h>
+
+#include "perfetto/ext/base/thread_task_runner.h"
+#include "perfetto/tracing/internal/tracing_tls.h"
+#include "perfetto/tracing/platform.h"
+
+#ifndef _WIN64
+#error 32-bit Windows is not supported
+#endif
+
+// Thread Termination Callbacks.
+// Windows doesn't support a per-thread destructor with its
+// TLS primitives. So, we build it manually by inserting a
+// function to be called on each thread's exit.
+// This magic is from chromium's base/threading/thread_local_storage_win.cc
+// which in turn is from http://www.codeproject.com/threads/tls.asp.
+
+#pragma comment(linker, "/INCLUDE:_tls_used")
+#pragma comment(linker, "/INCLUDE:perfetto_thread_callback_base")
+
+namespace perfetto {
+
+namespace {
+
+char g_tls_destroyed_sentinel; // Type is irrelevant, just need a ptr.
+
+class PlatformWindows : public Platform {
+ public:
+ static PlatformWindows* instance;
+ PlatformWindows();
+ ~PlatformWindows() override;
+
+ ThreadLocalObject* GetOrCreateThreadLocalObject() override;
+ std::unique_ptr<base::TaskRunner> CreateTaskRunner(
+ const CreateTaskRunnerArgs&) override;
+ std::string GetCurrentProcessName() override;
+ void OnThreadExit();
+
+ private:
+ DWORD tls_key_{};
+};
+
+using ThreadLocalObject = Platform::ThreadLocalObject;
+
+// static
+PlatformWindows* PlatformWindows::instance = nullptr;
+
+PlatformWindows::PlatformWindows() {
+ instance = this;
+ tls_key_ = ::TlsAlloc();
+ PERFETTO_CHECK(tls_key_ != TLS_OUT_OF_INDEXES);
+}
+
+PlatformWindows::~PlatformWindows() {
+ ::TlsFree(tls_key_);
+ instance = nullptr;
+}
+
+void PlatformWindows::OnThreadExit() {
+ auto tls = static_cast<ThreadLocalObject*>(::TlsGetValue(tls_key_));
+ if (tls) {
+ ::TlsSetValue(tls_key_, &g_tls_destroyed_sentinel);
+ delete tls;
+ }
+}
+
+ThreadLocalObject* PlatformWindows::GetOrCreateThreadLocalObject() {
+ void* tls_ptr = ::TlsGetValue(tls_key_);
+
+ // Detect attempts of re-creating the TLS while destroying it.
+ PERFETTO_CHECK(tls_ptr != &g_tls_destroyed_sentinel);
+
+ auto* tls = static_cast<ThreadLocalObject*>(tls_ptr);
+ if (!tls) {
+ tls = ThreadLocalObject::CreateInstance().release();
+ ::TlsSetValue(tls_key_, tls);
+ }
+ return tls;
+}
+
+std::unique_ptr<base::TaskRunner> PlatformWindows::CreateTaskRunner(
+ const CreateTaskRunnerArgs&) {
+ return std::unique_ptr<base::TaskRunner>(
+ new base::ThreadTaskRunner(base::ThreadTaskRunner::CreateAndStart()));
+}
+
+std::string PlatformWindows::GetCurrentProcessName() {
+ char buf[MAX_PATH];
+ auto len = ::GetModuleFileNameA(nullptr /*current*/, buf, sizeof(buf));
+ std::string name(buf, static_cast<size_t>(len));
+ size_t sep = name.find_last_of('\\');
+ if (sep != std::string::npos)
+ name = name.substr(sep + 1);
+ return name;
+}
+
+} // namespace
+
+// static
+Platform* Platform::GetDefaultPlatform() {
+ static PlatformWindows* thread_safe_init_instance = new PlatformWindows();
+ return thread_safe_init_instance;
+}
+
+} // namespace perfetto
+
+// -----------------------
+// Thread-local destructor
+// -----------------------
+
+// .CRT$XLA to .CRT$XLZ is an array of PIMAGE_TLS_CALLBACK pointers that are
+// called automatically by the OS loader code (not the CRT) when the module is
+// loaded and on thread creation. They are NOT called if the module has been
+// loaded by a LoadLibrary() call. It must have implicitly been loaded at
+// process startup.
+// See VC\crt\src\tlssup.c for reference.
+
+// extern "C" suppresses C++ name mangling so we know the symbol name for the
+// linker /INCLUDE:symbol pragma above.
+extern "C" {
+// The linker must not discard perfetto_thread_callback_base. (We force a
+// reference to this variable with a linker /INCLUDE:symbol pragma to ensure
+// that.) If this variable is discarded, the OnThreadExit function will never be
+// called.
+
+void NTAPI PerfettoOnThreadExit(PVOID, DWORD, PVOID);
+void NTAPI PerfettoOnThreadExit(PVOID module, DWORD reason, PVOID reserved) {
+ if (reason == DLL_THREAD_DETACH || reason == DLL_PROCESS_DETACH) {
+ if (perfetto::PlatformWindows::instance)
+ perfetto::PlatformWindows::instance->OnThreadExit();
+ }
+}
+
+// .CRT section is merged with .rdata on x64 so it must be constant data.
+#pragma const_seg(".CRT$XLP")
+
+// When defining a const variable, it must have external linkage to be sure the
+// linker doesn't discard it.
+extern const PIMAGE_TLS_CALLBACK perfetto_thread_callback_base;
+const PIMAGE_TLS_CALLBACK perfetto_thread_callback_base = PerfettoOnThreadExit;
+
+// Reset the default section.
+#pragma const_seg()
+} // extern "C"
+
+#endif // OS_WIN
diff --git a/test/test_helper.h b/test/test_helper.h
index 4ac4c46..d9a99b9 100644
--- a/test/test_helper.h
+++ b/test/test_helper.h
@@ -34,8 +34,7 @@
#include "test/fake_producer.h"
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-// TODO(primiano): uncomment in next CL.
-// #include "src/tracing/ipc/shared_memory_windows.h"
+#include "src/tracing/ipc/shared_memory_windows.h"
#else
#include "src/traced/probes/probes_producer.h"
#include "src/tracing/ipc/posix_shared_memory.h"
@@ -168,12 +167,12 @@
void CreateProducerProvidedSmb() {
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
- // TODO(primiano): in next CLs introduce SharedMemoryWindows.
+ SharedMemoryWindows::Factory factory;
#else
PosixSharedMemory::Factory factory;
+#endif
shm_ = factory.CreateSharedMemory(1024 * 1024);
shm_arbiter_ = SharedMemoryArbiter::CreateUnboundInstance(shm_.get(), 4096);
-#endif
}
void ProduceStartupEventBatch(const protos::gen::TestConfig& config,
diff --git a/tools/trace_to_text/trace_to_profile.cc b/tools/trace_to_text/trace_to_profile.cc
index a394e2b..b9e6807 100644
--- a/tools/trace_to_text/trace_to_profile.cc
+++ b/tools/trace_to_text/trace_to_profile.cc
@@ -53,10 +53,9 @@
std::string GetRandomString(size_t n) {
std::random_device r;
auto rng = std::default_random_engine(r());
- std::uniform_int_distribution<char> dist('a', 'z');
std::string result(n, ' ');
for (size_t i = 0; i < n; ++i) {
- result[i] = dist(rng);
+ result[i] = 'a' + (rng() % ('z' - 'a'));
}
return result;
}