Merge "TraceProcessor: add /websocket endpoint"
diff --git a/Android.bp b/Android.bp
index afc122e..3cb4471 100644
--- a/Android.bp
+++ b/Android.bp
@@ -92,6 +92,7 @@
":perfetto_src_tracing_common",
":perfetto_src_tracing_core_core",
":perfetto_src_tracing_ipc_common",
+ ":perfetto_src_tracing_ipc_default_socket",
":perfetto_src_tracing_ipc_producer_producer",
"src/profiling/memory/main.cc",
],
@@ -347,6 +348,7 @@
":perfetto_src_tracing_common",
":perfetto_src_tracing_core_core",
":perfetto_src_tracing_ipc_common",
+ ":perfetto_src_tracing_ipc_default_socket",
":perfetto_src_tracing_ipc_producer_producer",
],
shared_libs: [
@@ -548,6 +550,7 @@
":perfetto_src_tracing_core_service",
":perfetto_src_tracing_ipc_common",
":perfetto_src_tracing_ipc_consumer_consumer",
+ ":perfetto_src_tracing_ipc_default_socket",
":perfetto_src_tracing_ipc_producer_producer",
":perfetto_src_tracing_ipc_service_service",
],
@@ -732,6 +735,7 @@
":perfetto_src_tracing_in_process_backend",
":perfetto_src_tracing_ipc_common",
":perfetto_src_tracing_ipc_consumer_consumer",
+ ":perfetto_src_tracing_ipc_default_socket",
":perfetto_src_tracing_ipc_producer_producer",
":perfetto_src_tracing_ipc_service_service",
":perfetto_src_tracing_platform_impl",
@@ -924,6 +928,7 @@
":perfetto_src_tracing_core_core",
":perfetto_src_tracing_ipc_common",
":perfetto_src_tracing_ipc_consumer_consumer",
+ ":perfetto_src_tracing_ipc_default_socket",
":perfetto_src_tracing_ipc_producer_producer",
"src/perfetto_cmd/main.cc",
],
@@ -1099,6 +1104,7 @@
":perfetto_src_tracing_core_service",
":perfetto_src_tracing_ipc_common",
":perfetto_src_tracing_ipc_consumer_consumer",
+ ":perfetto_src_tracing_ipc_default_socket",
":perfetto_src_tracing_ipc_producer_producer",
":perfetto_src_tracing_ipc_service_service",
":perfetto_test_end_to_end_integrationtests",
@@ -1355,6 +1361,7 @@
":perfetto_src_tracing_core_service",
":perfetto_src_tracing_ipc_common",
":perfetto_src_tracing_ipc_consumer_consumer",
+ ":perfetto_src_tracing_ipc_default_socket",
":perfetto_src_tracing_ipc_producer_producer",
":perfetto_src_tracing_ipc_service_service",
":perfetto_test_test_helper",
@@ -1796,6 +1803,7 @@
":perfetto_src_tracing_in_process_backend",
":perfetto_src_tracing_ipc_common",
":perfetto_src_tracing_ipc_consumer_consumer",
+ ":perfetto_src_tracing_ipc_default_socket",
":perfetto_src_tracing_ipc_producer_producer",
":perfetto_src_tracing_ipc_service_service",
":perfetto_src_tracing_platform_impl",
@@ -8923,7 +8931,6 @@
filegroup {
name: "perfetto_src_tracing_ipc_common",
srcs: [
- "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",
@@ -8938,6 +8945,14 @@
],
}
+// GN: //src/tracing/ipc:default_socket
+filegroup {
+ name: "perfetto_src_tracing_ipc_default_socket",
+ srcs: [
+ "src/tracing/ipc/default_socket.cc",
+ ],
+}
+
// GN: //src/tracing/ipc/producer:producer
filegroup {
name: "perfetto_src_tracing_ipc_producer_producer",
@@ -9463,6 +9478,7 @@
":perfetto_src_tracing_core_unittests",
":perfetto_src_tracing_ipc_common",
":perfetto_src_tracing_ipc_consumer_consumer",
+ ":perfetto_src_tracing_ipc_default_socket",
":perfetto_src_tracing_ipc_producer_producer",
":perfetto_src_tracing_ipc_service_service",
":perfetto_src_tracing_ipc_unittests",
@@ -10037,6 +10053,7 @@
":perfetto_src_tracing_core_core",
":perfetto_src_tracing_core_service",
":perfetto_src_tracing_ipc_common",
+ ":perfetto_src_tracing_ipc_default_socket",
":perfetto_src_tracing_ipc_producer_producer",
"src/profiling/perf/main.cc",
],
@@ -10201,6 +10218,7 @@
":perfetto_src_tracing_common",
":perfetto_src_tracing_core_core",
":perfetto_src_tracing_ipc_common",
+ ":perfetto_src_tracing_ipc_default_socket",
":perfetto_src_tracing_ipc_producer_producer",
"src/perfetto_cmd/trigger_perfetto_main.cc",
],
diff --git a/BUILD b/BUILD
index a0d0884..de1511e 100644
--- a/BUILD
+++ b/BUILD
@@ -244,6 +244,7 @@
":src_tracing_core_service",
":src_tracing_ipc_common",
":src_tracing_ipc_consumer_consumer",
+ ":src_tracing_ipc_default_socket",
":src_tracing_ipc_producer_producer",
":src_tracing_ipc_service_service",
],
@@ -1810,7 +1811,6 @@
perfetto_filegroup(
name = "src_tracing_ipc_common",
srcs = [
- "src/tracing/ipc/default_socket.cc",
"src/tracing/ipc/memfd.cc",
"src/tracing/ipc/memfd.h",
"src/tracing/ipc/posix_shared_memory.cc",
@@ -1820,6 +1820,14 @@
],
)
+# GN target: //src/tracing/ipc:default_socket
+perfetto_filegroup(
+ name = "src_tracing_ipc_default_socket",
+ srcs = [
+ "src/tracing/ipc/default_socket.cc",
+ ],
+)
+
# GN target: //src/tracing:client_api_without_backends
perfetto_filegroup(
name = "src_tracing_client_api_without_backends",
@@ -3559,6 +3567,7 @@
":src_tracing_in_process_backend",
":src_tracing_ipc_common",
":src_tracing_ipc_consumer_consumer",
+ ":src_tracing_ipc_default_socket",
":src_tracing_ipc_producer_producer",
":src_tracing_ipc_service_service",
":src_tracing_platform_impl",
@@ -3651,6 +3660,7 @@
":src_tracing_core_core",
":src_tracing_ipc_common",
":src_tracing_ipc_consumer_consumer",
+ ":src_tracing_ipc_default_socket",
":src_tracing_ipc_producer_producer",
"src/perfetto_cmd/main.cc",
],
diff --git a/BUILD.gn b/BUILD.gn
index fe44ed2..b36d588 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -68,7 +68,10 @@
}
if (enable_perfetto_tools) {
- all_targets += [ "tools" ]
+ all_targets += [
+ "tools",
+ "src/websocket_bridge",
+ ]
}
if (enable_perfetto_unittests) {
diff --git a/CHANGELOG b/CHANGELOG
index e316684..3bf07a9 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -4,6 +4,9 @@
* Changed compiler flags. Assume recent x64 CPUs (-msse4.2 -mavx -mpopcnt).
This behavior affects only standalone builds and can be changed by setting
enable_perfetto_x64_cpu_opt=false in the GN args.
+ * The java heap profiler now rescans all the processes every time for
+ continous_dump_config. The scan_pids_only_on_start can be used to restore
+ the old behavior.
Trace Processor:
* Changed LIKE comparisions to be case-senstive. This may break existing
queries but was a necessary from a performance perspective.
diff --git a/protos/perfetto/config/perfetto_config.proto b/protos/perfetto/config/perfetto_config.proto
index 3e1caf8..52f256a 100644
--- a/protos/perfetto/config/perfetto_config.proto
+++ b/protos/perfetto/config/perfetto_config.proto
@@ -785,6 +785,13 @@
optional uint32 dump_phase_ms = 1;
// ms to wait between following dumps.
optional uint32 dump_interval_ms = 2;
+ // If true, scans all the processes to find `process_cmdline` and filter by
+ // `min_anonymous_memory_kb` only at data source start. Default on Android
+ // S-.
+ //
+ // If false, rescans all the processes to find on every dump. Default on
+ // Android T+.
+ optional bool scan_pids_only_on_start = 3;
}
// This input is normalized in the following way: if it contains slashes,
diff --git a/protos/perfetto/config/profiling/java_hprof_config.proto b/protos/perfetto/config/profiling/java_hprof_config.proto
index d504678..9245ee6 100644
--- a/protos/perfetto/config/profiling/java_hprof_config.proto
+++ b/protos/perfetto/config/profiling/java_hprof_config.proto
@@ -27,6 +27,13 @@
optional uint32 dump_phase_ms = 1;
// ms to wait between following dumps.
optional uint32 dump_interval_ms = 2;
+ // If true, scans all the processes to find `process_cmdline` and filter by
+ // `min_anonymous_memory_kb` only at data source start. Default on Android
+ // S-.
+ //
+ // If false, rescans all the processes to find on every dump. Default on
+ // Android T+.
+ optional bool scan_pids_only_on_start = 3;
}
// This input is normalized in the following way: if it contains slashes,
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index f8f8f94..c4eb417 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -785,6 +785,13 @@
optional uint32 dump_phase_ms = 1;
// ms to wait between following dumps.
optional uint32 dump_interval_ms = 2;
+ // If true, scans all the processes to find `process_cmdline` and filter by
+ // `min_anonymous_memory_kb` only at data source start. Default on Android
+ // S-.
+ //
+ // If false, rescans all the processes to find on every dump. Default on
+ // Android T+.
+ optional bool scan_pids_only_on_start = 3;
}
// This input is normalized in the following way: if it contains slashes,
diff --git a/src/profiling/memory/java_hprof_producer.cc b/src/profiling/memory/java_hprof_producer.cc
index aef9b5b..6c68995 100644
--- a/src/profiling/memory/java_hprof_producer.cc
+++ b/src/profiling/memory/java_hprof_producer.cc
@@ -40,8 +40,11 @@
auto it = data_sources_.find(id);
if (it == data_sources_.end())
return;
- const DataSource& ds = it->second;
- SignalDataSource(ds);
+ DataSource& ds = it->second;
+ if (!ds.config().continuous_dump_config().scan_pids_only_on_start()) {
+ ds.CollectPids();
+ }
+ ds.SendSignal();
auto weak_producer = weak_factory_.GetWeakPtr();
task_runner_->PostDelayedTask(
[weak_producer, id, dump_interval] {
@@ -52,10 +55,16 @@
dump_interval);
}
-// static
-void JavaHprofProducer::SignalDataSource(const DataSource& ds) {
- const std::set<pid_t>& pids = ds.pids;
- for (pid_t pid : pids) {
+JavaHprofProducer::DataSource::DataSource(
+ DataSourceConfig ds_config,
+ JavaHprofConfig config,
+ std::vector<std::string> normalized_cmdlines)
+ : ds_config_(ds_config),
+ config_(config),
+ normalized_cmdlines_(normalized_cmdlines) {}
+
+void JavaHprofProducer::DataSource::SendSignal() const {
+ for (pid_t pid : pids_) {
auto opt_status = ReadStatus(pid);
if (!opt_status) {
PERFETTO_PLOG("Failed to read /proc/%d/status. Not signalling.", pid);
@@ -69,23 +78,32 @@
pid);
continue;
}
- if (!CanProfile(ds.ds_config, uids->effective,
- ds.config.target_installed_by())) {
+ if (!CanProfile(ds_config_, uids->effective,
+ config_.target_installed_by())) {
PERFETTO_ELOG("%d (UID %" PRIu64 ") not profileable.", pid,
uids->effective);
continue;
}
PERFETTO_DLOG("Sending %d to %d", kJavaHeapprofdSignal, pid);
union sigval signal_value;
- signal_value.sival_int =
- static_cast<int32_t>(ds.ds_config.tracing_session_id() %
- std::numeric_limits<int32_t>::max());
+ signal_value.sival_int = static_cast<int32_t>(
+ ds_config_.tracing_session_id() % std::numeric_limits<int32_t>::max());
if (sigqueue(pid, kJavaHeapprofdSignal, signal_value) != 0) {
PERFETTO_DPLOG("sigqueue");
}
}
}
+void JavaHprofProducer::DataSource::CollectPids() {
+ pids_.clear();
+ for (uint64_t pid : config_.pid()) {
+ pids_.emplace(static_cast<pid_t>(pid));
+ }
+ FindPidsForCmdlines(normalized_cmdlines_, &pids_);
+ if (config_.min_anonymous_memory_kb() > 0)
+ RemoveUnderAnonThreshold(config_.min_anonymous_memory_kb(), &pids_);
+}
+
void JavaHprofProducer::IncreaseConnectionBackoff() {
connection_backoff_ms_ *= 2;
if (connection_backoff_ms_ > kMaxConnectionBackoffMs)
@@ -104,23 +122,15 @@
}
JavaHprofConfig config;
config.ParseFromString(ds_config.java_hprof_config_raw());
- DataSource ds;
- ds.id = id;
- for (uint64_t pid : config.pid())
- ds.pids.emplace(static_cast<pid_t>(pid));
base::Optional<std::vector<std::string>> normalized_cmdlines =
NormalizeCmdlines(config.process_cmdline());
if (!normalized_cmdlines.has_value()) {
PERFETTO_ELOG("Rejecting data source due to invalid cmdline in config.");
return;
}
- FindPidsForCmdlines(normalized_cmdlines.value(), &ds.pids);
- if (config.min_anonymous_memory_kb() > 0)
- RemoveUnderAnonThreshold(config.min_anonymous_memory_kb(), &ds.pids);
-
- ds.config = std::move(config);
- ds.ds_config = std::move(ds_config);
- data_sources_.emplace(id, std::move(ds));
+ DataSource ds(ds_config, std::move(config), std::move(*normalized_cmdlines));
+ ds.CollectPids();
+ data_sources_.emplace(id, ds);
}
void JavaHprofProducer::StartDataSource(DataSourceInstanceID id,
@@ -131,7 +141,7 @@
return;
}
const DataSource& ds = it->second;
- const auto continuous_dump_config = ds.config.continuous_dump_config();
+ const auto& continuous_dump_config = ds.config().continuous_dump_config();
uint32_t dump_interval = continuous_dump_config.dump_interval_ms();
if (dump_interval) {
auto weak_producer = weak_factory_.GetWeakPtr();
@@ -143,7 +153,7 @@
},
continuous_dump_config.dump_phase_ms());
}
- SignalDataSource(ds);
+ ds.SendSignal();
}
void JavaHprofProducer::StopDataSource(DataSourceInstanceID id) {
diff --git a/src/profiling/memory/java_hprof_producer.h b/src/profiling/memory/java_hprof_producer.h
index 97ac07d..056c60a 100644
--- a/src/profiling/memory/java_hprof_producer.h
+++ b/src/profiling/memory/java_hprof_producer.h
@@ -69,11 +69,23 @@
kConnected,
};
- struct DataSource {
- DataSourceInstanceID id;
- std::set<pid_t> pids;
- JavaHprofConfig config;
- DataSourceConfig ds_config;
+ class DataSource {
+ public:
+ DataSource(DataSourceConfig ds_config,
+ JavaHprofConfig config,
+ std::vector<std::string> normalized_cmdlines);
+ void CollectPids();
+ void SendSignal() const;
+
+ const JavaHprofConfig& config() const { return config_; }
+ const DataSourceConfig& ds_config() const { return ds_config_; }
+
+ private:
+ DataSourceConfig ds_config_;
+ JavaHprofConfig config_;
+ std::vector<std::string> normalized_cmdlines_;
+
+ std::set<pid_t> pids_;
};
void ConnectService();
@@ -82,7 +94,6 @@
void IncreaseConnectionBackoff();
void DoContinuousDump(DataSourceInstanceID id, uint32_t dump_interval);
- static void SignalDataSource(const DataSource& ds);
// State of connection to the tracing service.
State state_ = kNotStarted;
diff --git a/src/tracebox/BUILD.gn b/src/tracebox/BUILD.gn
index 647ad5e..9000732 100644
--- a/src/tracebox/BUILD.gn
+++ b/src/tracebox/BUILD.gn
@@ -24,6 +24,7 @@
"../perfetto_cmd:trigger_perfetto_cmd",
"../traced/probes",
"../traced/service",
+ "../websocket_bridge:lib",
]
sources = [ "tracebox.cc" ]
}
diff --git a/src/tracebox/tracebox.cc b/src/tracebox/tracebox.cc
index ed75b31..a421b97 100644
--- a/src/tracebox/tracebox.cc
+++ b/src/tracebox/tracebox.cc
@@ -20,6 +20,7 @@
#include "perfetto/ext/base/utils.h"
#include "perfetto/ext/traced/traced.h"
#include "src/perfetto_cmd/perfetto_cmd.h"
+#include "src/websocket_bridge/websocket_bridge.h"
#include <stdio.h>
@@ -39,6 +40,7 @@
{"traced_probes", ProbesMain},
{"perfetto", PerfettoCmdMain},
{"trigger_perfetto", TriggerPerfettoMain},
+ {"websocket_bridge", WebsocketBridgeMain},
};
void PrintUsage() {
diff --git a/src/tracing/ipc/BUILD.gn b/src/tracing/ipc/BUILD.gn
index f86aeb5..dfd0eda 100644
--- a/src/tracing/ipc/BUILD.gn
+++ b/src/tracing/ipc/BUILD.gn
@@ -27,7 +27,6 @@
"../../../include/perfetto/ext/tracing/ipc",
]
sources = [
- "default_socket.cc",
"memfd.cc",
"memfd.h",
"posix_shared_memory.cc",
@@ -36,6 +35,7 @@
"shared_memory_windows.h",
]
deps = [
+ ":default_socket",
"../../../gn:default_deps",
"../../../include/perfetto/ext/ipc",
"../../base",
@@ -43,6 +43,17 @@
]
}
+source_set("default_socket") {
+ sources = [ "default_socket.cc" ]
+ public_deps = [ "../../../include/perfetto/ext/tracing/ipc" ]
+ deps = [
+ "../../../gn:default_deps",
+ "../../../include/perfetto/ext/ipc",
+ "../../../include/perfetto/ext/tracing/core",
+ "../../base",
+ ]
+}
+
perfetto_unittest_source_set("unittests") {
testonly = true
deps = [
diff --git a/src/websocket_bridge/BUILD.gn b/src/websocket_bridge/BUILD.gn
new file mode 100644
index 0000000..38f3e66
--- /dev/null
+++ b/src/websocket_bridge/BUILD.gn
@@ -0,0 +1,35 @@
+# 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.
+
+source_set("lib") {
+ deps = [
+ "../../gn:default_deps",
+ "../base",
+ "../base:unix_socket",
+ "../base/http",
+ "../tracing/ipc:default_socket",
+ ]
+ sources = [
+ "websocket_bridge.cc",
+ "websocket_bridge.h",
+ ]
+}
+
+executable("websocket_bridge") {
+ deps = [
+ ":lib",
+ "../../gn:default_deps",
+ ]
+ sources = [ "websocket_bridge_main.cc" ]
+}
diff --git a/src/websocket_bridge/websocket_bridge.cc b/src/websocket_bridge/websocket_bridge.cc
new file mode 100644
index 0000000..eb26506
--- /dev/null
+++ b/src/websocket_bridge/websocket_bridge.cc
@@ -0,0 +1,190 @@
+/*
+ * 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/websocket_bridge/websocket_bridge.h"
+
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "perfetto/ext/base/http/http_server.h"
+#include "perfetto/ext/base/unix_socket.h"
+#include "perfetto/ext/base/unix_task_runner.h"
+#include "perfetto/ext/tracing/ipc/default_socket.h"
+
+namespace perfetto {
+namespace {
+
+constexpr int kWebsocketPort = 8037;
+
+struct Endpoint {
+ const char* uri;
+ const char* endpoint;
+ base::SockFamily family;
+};
+
+class WSBridge : public base::HttpRequestHandler,
+ public base::UnixSocket::EventListener {
+ public:
+ void Main(int argc, char** argv);
+
+ // base::HttpRequestHandler implementation.
+ void OnHttpRequest(const base::HttpRequest&) override;
+ void OnWebsocketMessage(const base::WebsocketMessage&) override;
+ void OnHttpConnectionClosed(base::HttpServerConnection*) override;
+
+ // base::UnixSocket::EventListener implementation.
+ void OnNewIncomingConnection(base::UnixSocket*,
+ std::unique_ptr<base::UnixSocket>) override;
+ void OnConnect(base::UnixSocket*, bool) override;
+ void OnDisconnect(base::UnixSocket*) override;
+ void OnDataAvailable(base::UnixSocket* self) override;
+
+ private:
+ base::HttpServerConnection* GetWebsocket(base::UnixSocket*);
+
+ base::UnixTaskRunner task_runner_;
+ std::vector<Endpoint> endpoints_;
+ std::map<base::HttpServerConnection*, std::unique_ptr<base::UnixSocket>>
+ conns_;
+};
+
+void WSBridge::Main(int, char**) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ // On Windows traced used a TCP socket.
+ const auto kTracedFamily = base::SockFamily::kInet;
+#else
+ const auto kTracedFamily = base::SockFamily::kUnix;
+#endif
+ endpoints_.push_back({"/traced", GetConsumerSocket(), kTracedFamily});
+ endpoints_.push_back({"/adb", "127.0.0.1:5037", base::SockFamily::kInet});
+
+ base::HttpServer srv(&task_runner_, this);
+ srv.AddAllowedOrigin("http://localhost:10000");
+ srv.AddAllowedOrigin("http://127.0.0.1:10000");
+ srv.AddAllowedOrigin("https://ui.perfetto.dev");
+
+ srv.Start(kWebsocketPort);
+ PERFETTO_LOG("[WSBridge] Listening on 127.0.0.1:%d", kWebsocketPort);
+ task_runner_.Run();
+}
+
+void WSBridge::OnHttpRequest(const base::HttpRequest& req) {
+ for (const auto& ep : endpoints_) {
+ if (req.uri != ep.uri || !req.is_websocket_handshake)
+ continue;
+
+ // Connect to the endpoint in blocking mode.
+ auto sock_raw =
+ base::UnixSocketRaw::CreateMayFail(ep.family, base::SockType::kStream);
+ if (!sock_raw) {
+ PERFETTO_PLOG("socket() failed");
+ req.conn->SendResponseAndClose("500 Server Error");
+ return;
+ }
+ PERFETTO_LOG("[WSBridge] New connection from \"%.*s\"",
+ static_cast<int>(req.origin.size()), req.origin.data());
+ sock_raw.SetTxTimeout(3000);
+ sock_raw.SetBlocking(true);
+
+ if (!sock_raw.Connect(ep.endpoint)) {
+ PERFETTO_ELOG("[WSBridge] Connection to %s failed", ep.endpoint);
+ req.conn->SendResponseAndClose("503 Service Unavailable");
+ return;
+ }
+ sock_raw.SetBlocking(false);
+
+ PERFETTO_DLOG("[WSBridge] Connected to %s", ep.endpoint);
+ conns_[req.conn] = base::UnixSocket::AdoptConnected(
+ sock_raw.ReleaseFd(), this, &task_runner_, ep.family,
+ base::SockType::kStream);
+
+ req.conn->UpgradeToWebsocket(req);
+ return;
+ } // for (endpoint)
+ req.conn->SendResponseAndClose("404 Not Found");
+}
+
+// Called when an inbound websocket message is received from the browser.
+void WSBridge::OnWebsocketMessage(const base::WebsocketMessage& msg) {
+ auto it = conns_.find(msg.conn);
+ PERFETTO_CHECK(it != conns_.end());
+ // Pass through the websocket message onto the endpoint TCP socket.
+ base::UnixSocket& sock = *it->second;
+ sock.Send(msg.data.data(), msg.data.size());
+}
+
+// Called when a TCP message is received from the endpoint.
+void WSBridge::OnDataAvailable(base::UnixSocket* sock) {
+ base::HttpServerConnection* websocket = GetWebsocket(sock);
+ PERFETTO_CHECK(websocket);
+
+ char buf[8192];
+ auto rsize = sock->Receive(buf, sizeof(buf));
+ if (rsize > 0) {
+ websocket->SendWebsocketMessage(buf, static_cast<size_t>(rsize));
+ } else {
+ // Connection closed or errored.
+ sock->Shutdown(/*notify=*/true); // Will trigger OnDisconnect().
+ websocket->Close();
+ }
+}
+
+// Called when the browser terminates the websocket connection.
+void WSBridge::OnHttpConnectionClosed(base::HttpServerConnection* websocket) {
+ PERFETTO_DLOG("[WSBridge] Websocket connection closed");
+ auto it = conns_.find(websocket);
+ if (it == conns_.end())
+ return; // Can happen if ADB closed first.
+ base::UnixSocket& sock = *it->second;
+ sock.Shutdown(/*notify=*/true);
+ conns_.erase(websocket);
+}
+
+void WSBridge::OnDisconnect(base::UnixSocket* sock) {
+ base::HttpServerConnection* websocket = GetWebsocket(sock);
+ if (!websocket)
+ return;
+ websocket->Close();
+ sock->Shutdown(/*notify=*/false);
+ conns_.erase(websocket);
+ PERFETTO_DLOG("[WSBridge] Socket connection closed");
+}
+
+base::HttpServerConnection* WSBridge::GetWebsocket(base::UnixSocket* sock) {
+ for (const auto& it : conns_) {
+ if (it.second.get() == sock) {
+ return it.first;
+ }
+ }
+ return nullptr;
+}
+
+void WSBridge::OnConnect(base::UnixSocket*, bool) {}
+void WSBridge::OnNewIncomingConnection(base::UnixSocket*,
+ std::unique_ptr<base::UnixSocket>) {}
+
+} // namespace
+
+int PERFETTO_EXPORT_ENTRYPOINT WebsocketBridgeMain(int argc, char** argv) {
+ perfetto::WSBridge ws_bridge;
+ ws_bridge.Main(argc, argv);
+ return 0;
+}
+
+} // namespace perfetto
diff --git a/src/websocket_bridge/websocket_bridge.h b/src/websocket_bridge/websocket_bridge.h
new file mode 100644
index 0000000..d93bd61
--- /dev/null
+++ b/src/websocket_bridge/websocket_bridge.h
@@ -0,0 +1,26 @@
+/*
+ * 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_WEBSOCKET_BRIDGE_WEBSOCKET_BRIDGE_H_
+#define SRC_WEBSOCKET_BRIDGE_WEBSOCKET_BRIDGE_H_
+
+#include "perfetto/base/compiler.h"
+
+namespace perfetto {
+int WebsocketBridgeMain(int argc, char** argv);
+}
+
+#endif // SRC_WEBSOCKET_BRIDGE_WEBSOCKET_BRIDGE_H_
diff --git a/src/websocket_bridge/websocket_bridge_main.cc b/src/websocket_bridge/websocket_bridge_main.cc
new file mode 100644
index 0000000..480522b
--- /dev/null
+++ b/src/websocket_bridge/websocket_bridge_main.cc
@@ -0,0 +1,23 @@
+/*
+ * 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/websocket_bridge/websocket_bridge.h"
+
+// This is split in a dedicated translation unit so that tracebox can refer to
+// WebsocketBridgeMain() without pulling in a main() symbol.
+int main(int argc, char** argv) {
+ return perfetto::WebsocketBridgeMain(argc, argv);
+}