Add ServerCallTracer interfaces (#32555)

<!--

If you know who should review your pull request, please assign it to
that
person, otherwise the pull request would get assigned randomly.

If your pull request is for a specific language, please add the
appropriate
lang label.

-->
pull/32605/head
Yash Tibrewal 2 years ago committed by GitHub
parent e81002cfda
commit 9551e3ef5a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      BUILD
  2. 42
      CMakeLists.txt
  3. 2
      Makefile
  4. 18
      build_autogenerated.yaml
  5. 1
      config.m4
  6. 1
      config.w32
  7. 2
      gRPC-C++.podspec
  8. 3
      gRPC-Core.podspec
  9. 2
      grpc.gemspec
  10. 3
      grpc.gyp
  11. 2
      package.xml
  12. 51
      src/core/lib/channel/server_call_tracer.cc
  13. 97
      src/core/lib/channel/server_call_tracer.h
  14. 1
      src/python/grpcio/grpc_core_dependencies.py
  15. 16
      test/core/channel/BUILD
  16. 49
      test/core/channel/server_call_tracer_factory_test.cc
  17. 2
      tools/doxygen/Doxyfile.c++.internal
  18. 2
      tools/doxygen/Doxyfile.core.internal
  19. 24
      tools/run_tests/generated/tests.json

@ -1217,6 +1217,7 @@ grpc_cc_library(
"//src/core:lib/channel/channelz_registry.cc",
"//src/core:lib/channel/connected_channel.cc",
"//src/core:lib/channel/promise_based_filter.cc",
"//src/core:lib/channel/server_call_tracer.cc",
"//src/core:lib/channel/status_util.cc",
"//src/core:lib/compression/compression.cc",
"//src/core:lib/compression/compression_internal.cc",
@ -1327,6 +1328,7 @@ grpc_cc_library(
"//src/core:lib/channel/connected_channel.h",
"//src/core:lib/channel/context.h",
"//src/core:lib/channel/promise_based_filter.h",
"//src/core:lib/channel/server_call_tracer.h",
"//src/core:lib/channel/status_util.h",
"//src/core:lib/compression/compression_internal.h",
"//src/core:lib/compression/message_compress.h",

42
CMakeLists.txt generated

@ -1150,6 +1150,7 @@ if(gRPC_BUILD_TESTS)
if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
add_dependencies(buildtests_cxx server_builder_with_socket_mutator_test)
endif()
add_dependencies(buildtests_cxx server_call_tracer_factory_test)
add_dependencies(buildtests_cxx server_chttp2_test)
add_dependencies(buildtests_cxx server_config_selector_test)
add_dependencies(buildtests_cxx server_context_test_spouse_test)
@ -2165,6 +2166,7 @@ add_library(grpc
src/core/lib/channel/channelz_registry.cc
src/core/lib/channel/connected_channel.cc
src/core/lib/channel/promise_based_filter.cc
src/core/lib/channel/server_call_tracer.cc
src/core/lib/channel/status_util.cc
src/core/lib/compression/compression.cc
src/core/lib/compression/compression_internal.cc
@ -2853,6 +2855,7 @@ add_library(grpc_unsecure
src/core/lib/channel/channelz_registry.cc
src/core/lib/channel/connected_channel.cc
src/core/lib/channel/promise_based_filter.cc
src/core/lib/channel/server_call_tracer.cc
src/core/lib/channel/status_util.cc
src/core/lib/compression/compression.cc
src/core/lib/compression/compression_internal.cc
@ -4371,6 +4374,7 @@ add_library(grpc_authorization_provider
src/core/lib/channel/channelz_registry.cc
src/core/lib/channel/connected_channel.cc
src/core/lib/channel/promise_based_filter.cc
src/core/lib/channel/server_call_tracer.cc
src/core/lib/channel/status_util.cc
src/core/lib/compression/compression.cc
src/core/lib/compression/compression_internal.cc
@ -11461,6 +11465,7 @@ add_executable(frame_test
src/core/lib/channel/channelz_registry.cc
src/core/lib/channel/connected_channel.cc
src/core/lib/channel/promise_based_filter.cc
src/core/lib/channel/server_call_tracer.cc
src/core/lib/channel/status_util.cc
src/core/lib/compression/compression.cc
src/core/lib/compression/compression_internal.cc
@ -18286,6 +18291,43 @@ endif()
endif()
if(gRPC_BUILD_TESTS)
add_executable(server_call_tracer_factory_test
test/core/channel/server_call_tracer_factory_test.cc
third_party/googletest/googletest/src/gtest-all.cc
third_party/googletest/googlemock/src/gmock-all.cc
)
target_compile_features(server_call_tracer_factory_test PUBLIC cxx_std_14)
target_include_directories(server_call_tracer_factory_test
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/include
${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
${_gRPC_RE2_INCLUDE_DIR}
${_gRPC_SSL_INCLUDE_DIR}
${_gRPC_UPB_GENERATED_DIR}
${_gRPC_UPB_GRPC_GENERATED_DIR}
${_gRPC_UPB_INCLUDE_DIR}
${_gRPC_XXHASH_INCLUDE_DIR}
${_gRPC_ZLIB_INCLUDE_DIR}
third_party/googletest/googletest/include
third_party/googletest/googletest
third_party/googletest/googlemock/include
third_party/googletest/googlemock
${_gRPC_PROTO_GENS_DIR}
)
target_link_libraries(server_call_tracer_factory_test
${_gRPC_BASELIB_LIBRARIES}
${_gRPC_PROTOBUF_LIBRARIES}
${_gRPC_ZLIB_LIBRARIES}
${_gRPC_ALLTARGETS_LIBRARIES}
grpc_test_util
)
endif()
if(gRPC_BUILD_TESTS)
add_executable(server_chttp2_test
test/core/surface/server_chttp2_test.cc
third_party/googletest/googletest/src/gtest-all.cc

2
Makefile generated

@ -1414,6 +1414,7 @@ LIBGRPC_SRC = \
src/core/lib/channel/channelz_registry.cc \
src/core/lib/channel/connected_channel.cc \
src/core/lib/channel/promise_based_filter.cc \
src/core/lib/channel/server_call_tracer.cc \
src/core/lib/channel/status_util.cc \
src/core/lib/compression/compression.cc \
src/core/lib/compression/compression_internal.cc \
@ -1955,6 +1956,7 @@ LIBGRPC_UNSECURE_SRC = \
src/core/lib/channel/channelz_registry.cc \
src/core/lib/channel/connected_channel.cc \
src/core/lib/channel/promise_based_filter.cc \
src/core/lib/channel/server_call_tracer.cc \
src/core/lib/channel/status_util.cc \
src/core/lib/compression/compression.cc \
src/core/lib/compression/compression_internal.cc \

@ -774,6 +774,7 @@ libs:
- src/core/lib/channel/connected_channel.h
- src/core/lib/channel/context.h
- src/core/lib/channel/promise_based_filter.h
- src/core/lib/channel/server_call_tracer.h
- src/core/lib/channel/status_util.h
- src/core/lib/compression/compression_internal.h
- src/core/lib/compression/message_compress.h
@ -1555,6 +1556,7 @@ libs:
- src/core/lib/channel/channelz_registry.cc
- src/core/lib/channel/connected_channel.cc
- src/core/lib/channel/promise_based_filter.cc
- src/core/lib/channel/server_call_tracer.cc
- src/core/lib/channel/status_util.cc
- src/core/lib/compression/compression.cc
- src/core/lib/compression/compression_internal.cc
@ -2114,6 +2116,7 @@ libs:
- src/core/lib/channel/connected_channel.h
- src/core/lib/channel/context.h
- src/core/lib/channel/promise_based_filter.h
- src/core/lib/channel/server_call_tracer.h
- src/core/lib/channel/status_util.h
- src/core/lib/compression/compression_internal.h
- src/core/lib/compression/message_compress.h
@ -2508,6 +2511,7 @@ libs:
- src/core/lib/channel/channelz_registry.cc
- src/core/lib/channel/connected_channel.cc
- src/core/lib/channel/promise_based_filter.cc
- src/core/lib/channel/server_call_tracer.cc
- src/core/lib/channel/status_util.cc
- src/core/lib/compression/compression.cc
- src/core/lib/compression/compression_internal.cc
@ -3575,6 +3579,7 @@ libs:
- src/core/lib/channel/connected_channel.h
- src/core/lib/channel/context.h
- src/core/lib/channel/promise_based_filter.h
- src/core/lib/channel/server_call_tracer.h
- src/core/lib/channel/status_util.h
- src/core/lib/compression/compression_internal.h
- src/core/lib/compression/message_compress.h
@ -3848,6 +3853,7 @@ libs:
- src/core/lib/channel/channelz_registry.cc
- src/core/lib/channel/connected_channel.cc
- src/core/lib/channel/promise_based_filter.cc
- src/core/lib/channel/server_call_tracer.cc
- src/core/lib/channel/status_util.cc
- src/core/lib/compression/compression.cc
- src/core/lib/compression/compression_internal.cc
@ -7394,6 +7400,7 @@ targets:
- src/core/lib/channel/connected_channel.h
- src/core/lib/channel/context.h
- src/core/lib/channel/promise_based_filter.h
- src/core/lib/channel/server_call_tracer.h
- src/core/lib/channel/status_util.h
- src/core/lib/compression/compression_internal.h
- src/core/lib/compression/message_compress.h
@ -7650,6 +7657,7 @@ targets:
- src/core/lib/channel/channelz_registry.cc
- src/core/lib/channel/connected_channel.cc
- src/core/lib/channel/promise_based_filter.cc
- src/core/lib/channel/server_call_tracer.cc
- src/core/lib/channel/status_util.cc
- src/core/lib/compression/compression.cc
- src/core/lib/compression/compression_internal.cc
@ -10797,6 +10805,16 @@ targets:
- linux
- posix
- mac
- name: server_call_tracer_factory_test
gtest: true
build: test
language: c++
headers: []
src:
- test/core/channel/server_call_tracer_factory_test.cc
deps:
- grpc_test_util
uses_polling: false
- name: server_chttp2_test
gtest: true
build: test

1
config.m4 generated

@ -495,6 +495,7 @@ if test "$PHP_GRPC" != "no"; then
src/core/lib/channel/channelz_registry.cc \
src/core/lib/channel/connected_channel.cc \
src/core/lib/channel/promise_based_filter.cc \
src/core/lib/channel/server_call_tracer.cc \
src/core/lib/channel/status_util.cc \
src/core/lib/compression/compression.cc \
src/core/lib/compression/compression_internal.cc \

1
config.w32 generated

@ -461,6 +461,7 @@ if (PHP_GRPC != "no") {
"src\\core\\lib\\channel\\channelz_registry.cc " +
"src\\core\\lib\\channel\\connected_channel.cc " +
"src\\core\\lib\\channel\\promise_based_filter.cc " +
"src\\core\\lib\\channel\\server_call_tracer.cc " +
"src\\core\\lib\\channel\\status_util.cc " +
"src\\core\\lib\\compression\\compression.cc " +
"src\\core\\lib\\compression\\compression_internal.cc " +

2
gRPC-C++.podspec generated

@ -725,6 +725,7 @@ Pod::Spec.new do |s|
'src/core/lib/channel/connected_channel.h',
'src/core/lib/channel/context.h',
'src/core/lib/channel/promise_based_filter.h',
'src/core/lib/channel/server_call_tracer.h',
'src/core/lib/channel/status_util.h',
'src/core/lib/compression/compression_internal.h',
'src/core/lib/compression/message_compress.h',
@ -1659,6 +1660,7 @@ Pod::Spec.new do |s|
'src/core/lib/channel/connected_channel.h',
'src/core/lib/channel/context.h',
'src/core/lib/channel/promise_based_filter.h',
'src/core/lib/channel/server_call_tracer.h',
'src/core/lib/channel/status_util.h',
'src/core/lib/compression/compression_internal.h',
'src/core/lib/compression/message_compress.h',

3
gRPC-Core.podspec generated

@ -1109,6 +1109,8 @@ Pod::Spec.new do |s|
'src/core/lib/channel/context.h',
'src/core/lib/channel/promise_based_filter.cc',
'src/core/lib/channel/promise_based_filter.h',
'src/core/lib/channel/server_call_tracer.cc',
'src/core/lib/channel/server_call_tracer.h',
'src/core/lib/channel/status_util.cc',
'src/core/lib/channel/status_util.h',
'src/core/lib/compression/compression.cc',
@ -2346,6 +2348,7 @@ Pod::Spec.new do |s|
'src/core/lib/channel/connected_channel.h',
'src/core/lib/channel/context.h',
'src/core/lib/channel/promise_based_filter.h',
'src/core/lib/channel/server_call_tracer.h',
'src/core/lib/channel/status_util.h',
'src/core/lib/compression/compression_internal.h',
'src/core/lib/compression/message_compress.h',

2
grpc.gemspec generated

@ -1018,6 +1018,8 @@ Gem::Specification.new do |s|
s.files += %w( src/core/lib/channel/context.h )
s.files += %w( src/core/lib/channel/promise_based_filter.cc )
s.files += %w( src/core/lib/channel/promise_based_filter.h )
s.files += %w( src/core/lib/channel/server_call_tracer.cc )
s.files += %w( src/core/lib/channel/server_call_tracer.h )
s.files += %w( src/core/lib/channel/status_util.cc )
s.files += %w( src/core/lib/channel/status_util.h )
s.files += %w( src/core/lib/compression/compression.cc )

3
grpc.gyp generated

@ -827,6 +827,7 @@
'src/core/lib/channel/channelz_registry.cc',
'src/core/lib/channel/connected_channel.cc',
'src/core/lib/channel/promise_based_filter.cc',
'src/core/lib/channel/server_call_tracer.cc',
'src/core/lib/channel/status_util.cc',
'src/core/lib/compression/compression.cc',
'src/core/lib/compression/compression_internal.cc',
@ -1310,6 +1311,7 @@
'src/core/lib/channel/channelz_registry.cc',
'src/core/lib/channel/connected_channel.cc',
'src/core/lib/channel/promise_based_filter.cc',
'src/core/lib/channel/server_call_tracer.cc',
'src/core/lib/channel/status_util.cc',
'src/core/lib/compression/compression.cc',
'src/core/lib/compression/compression_internal.cc',
@ -1817,6 +1819,7 @@
'src/core/lib/channel/channelz_registry.cc',
'src/core/lib/channel/connected_channel.cc',
'src/core/lib/channel/promise_based_filter.cc',
'src/core/lib/channel/server_call_tracer.cc',
'src/core/lib/channel/status_util.cc',
'src/core/lib/compression/compression.cc',
'src/core/lib/compression/compression_internal.cc',

2
package.xml generated

@ -1000,6 +1000,8 @@
<file baseinstalldir="/" name="src/core/lib/channel/context.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/channel/promise_based_filter.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/channel/promise_based_filter.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/channel/server_call_tracer.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/channel/server_call_tracer.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/channel/status_util.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/channel/status_util.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/compression/compression.cc" role="src" />

@ -0,0 +1,51 @@
//
//
// Copyright 2023 gRPC authors.
//
// 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 <grpc/support/port_platform.h>
#include "src/core/lib/channel/server_call_tracer.h"
namespace grpc_core {
//
// ServerCallTracerFactory
//
namespace {
ServerCallTracerFactory* g_server_call_tracer_factory_ = nullptr;
const char* kServerCallTracerFactoryChannelArgName =
"grpc.experimental.server_call_tracer_factory";
} // namespace
ServerCallTracerFactory* ServerCallTracerFactory::Get(
const ChannelArgs& channel_args) {
ServerCallTracerFactory* factory =
channel_args.GetObject<ServerCallTracerFactory>();
return factory != nullptr ? factory : g_server_call_tracer_factory_;
}
void ServerCallTracerFactory::RegisterGlobal(ServerCallTracerFactory* factory) {
g_server_call_tracer_factory_ = factory;
}
absl::string_view ServerCallTracerFactory::ChannelArgName() {
return kServerCallTracerFactoryChannelArgName;
}
} // namespace grpc_core

@ -0,0 +1,97 @@
//
//
// Copyright 2023 gRPC authors.
//
// 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 GRPC_SRC_CORE_LIB_CHANNEL_SERVER_CALL_TRACER_H
#define GRPC_SRC_CORE_LIB_CHANNEL_SERVER_CALL_TRACER_H
#include <grpc/support/port_platform.h>
#include <stdint.h>
#include "absl/status/status.h"
#include "absl/strings/string_view.h"
#include <grpc/support/time.h>
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/iomgr/error.h"
#include "src/core/lib/slice/slice_buffer.h"
#include "src/core/lib/transport/metadata_batch.h"
#include "src/core/lib/transport/transport.h"
namespace grpc_core {
// Interface for a tracer that records activities on a server call.
class ServerCallTracer {
public:
virtual ~ServerCallTracer() {}
// Please refer to `grpc_transport_stream_op_batch_payload` for details on
// arguments.
virtual void RecordSendInitialMetadata(
grpc_metadata_batch* send_initial_metadata) = 0;
virtual void RecordSendTrailingMetadata(
grpc_metadata_batch* send_trailing_metadata) = 0;
virtual void RecordSendMessage(const SliceBuffer& send_message) = 0;
// The `RecordReceivedInitialMetadata()` and `RecordReceivedMessage()`
// methods should only be invoked when the metadata/message was
// successfully received, i.e., without any error.
virtual void RecordReceivedInitialMetadata(
grpc_metadata_batch* recv_initial_metadata, uint32_t flags) = 0;
virtual void RecordReceivedMessage(const SliceBuffer& recv_message) = 0;
// If the call was cancelled before the recv_trailing_metadata op
// was started, recv_trailing_metadata and transport_stream_stats
// will be null.
virtual void RecordReceivedTrailingMetadata(
absl::Status status, grpc_metadata_batch* recv_trailing_metadata,
const grpc_transport_stream_stats* transport_stream_stats) = 0;
virtual void RecordCancel(grpc_error_handle cancel_error) = 0;
// Should be the last API call to the object. Once invoked, the tracer
// library is free to destroy the object.
virtual void RecordEnd(const gpr_timespec& latency) = 0;
// Records an annotation on the call attempt.
// TODO(yashykt): If needed, extend this to attach attributes with
// annotations.
virtual void RecordAnnotation(absl::string_view annotation) = 0;
};
// Interface for a factory that can create a ServerCallTracer object per server
// call.
class ServerCallTracerFactory {
public:
struct RawPointerChannelArgTag {};
virtual ~ServerCallTracerFactory() {}
virtual ServerCallTracer* CreateNewServerCallTracer() = 0;
// Use this method to get the server call tracer factory from channel args,
// instead of directly fetching it with `GetObject`.
static ServerCallTracerFactory* Get(const ChannelArgs& channel_args);
// Registers a global ServerCallTracerFactory that wil be used by default if
// no corresponding channel arg was found. It is only valid to call this
// before grpc_init(). It is the responsibility of the caller to maintain this
// for the lifetime of the process.
static void RegisterGlobal(ServerCallTracerFactory* factory);
static absl::string_view ChannelArgName();
};
} // namespace grpc_core
#endif // GRPC_SRC_CORE_LIB_CHANNEL_SERVER_CALL_TRACER_H

@ -470,6 +470,7 @@ CORE_SOURCE_FILES = [
'src/core/lib/channel/channelz_registry.cc',
'src/core/lib/channel/connected_channel.cc',
'src/core/lib/channel/promise_based_filter.cc',
'src/core/lib/channel/server_call_tracer.cc',
'src/core/lib/channel/status_util.cc',
'src/core/lib/compression/compression.cc',
'src/core/lib/compression/compression_internal.cc',

@ -161,3 +161,19 @@ grpc_cc_test(
"//test/core/util:grpc_test_util",
],
)
grpc_cc_test(
name = "server_call_tracer_factory_test",
srcs = ["server_call_tracer_factory_test.cc"],
external_deps = [
"gtest",
],
language = "C++",
uses_event_engine = False,
uses_polling = False,
deps = [
"//:gpr",
"//:grpc",
"//test/core/util:grpc_test_util",
],
)

@ -0,0 +1,49 @@
// Copyright 2023 gRPC authors.
//
// 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 "gtest/gtest.h"
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/channel/server_call_tracer.h"
#include "src/core/lib/gprpp/crash.h"
namespace grpc_core {
namespace {
class TestServerCallTracerFactory : public ServerCallTracerFactory {
public:
ServerCallTracer* CreateNewServerCallTracer() override {
Crash("Not implemented");
}
};
TEST(ServerCallTracerFactoryTest, GlobalRegistration) {
TestServerCallTracerFactory factory;
ServerCallTracerFactory::RegisterGlobal(&factory);
EXPECT_EQ(ServerCallTracerFactory::Get(ChannelArgs()), &factory);
}
TEST(ServerCallTracerFactoryTest, UsingChannelArgs) {
TestServerCallTracerFactory factory;
ChannelArgs args = ChannelArgs().SetObject(&factory);
EXPECT_EQ(ServerCallTracerFactory::Get(args), &factory);
}
} // namespace
} // namespace grpc_core
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

@ -2013,6 +2013,8 @@ src/core/lib/channel/connected_channel.h \
src/core/lib/channel/context.h \
src/core/lib/channel/promise_based_filter.cc \
src/core/lib/channel/promise_based_filter.h \
src/core/lib/channel/server_call_tracer.cc \
src/core/lib/channel/server_call_tracer.h \
src/core/lib/channel/status_util.cc \
src/core/lib/channel/status_util.h \
src/core/lib/compression/compression.cc \

@ -1791,6 +1791,8 @@ src/core/lib/channel/connected_channel.h \
src/core/lib/channel/context.h \
src/core/lib/channel/promise_based_filter.cc \
src/core/lib/channel/promise_based_filter.h \
src/core/lib/channel/server_call_tracer.cc \
src/core/lib/channel/server_call_tracer.h \
src/core/lib/channel/status_util.cc \
src/core/lib/channel/status_util.h \
src/core/lib/compression/compression.cc \

@ -6321,6 +6321,30 @@
],
"uses_polling": true
},
{
"args": [],
"benchmark": false,
"ci_platforms": [
"linux",
"mac",
"posix",
"windows"
],
"cpu_cost": 1.0,
"exclude_configs": [],
"exclude_iomgrs": [],
"flaky": false,
"gtest": true,
"language": "c++",
"name": "server_call_tracer_factory_test",
"platforms": [
"linux",
"mac",
"posix",
"windows"
],
"uses_polling": false
},
{
"args": [],
"benchmark": false,

Loading…
Cancel
Save