From ff87ca02b2f1229e42cdccea47655cc630634b92 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Tue, 22 Feb 2022 15:39:40 -0800 Subject: [PATCH] Revive test/core/memory_usage (#28931) * Revert "Deprecate gpr_get/set_allocation_functions" This reverts commit 81df105ac89467ba2b9d150e73709f0a7f0ffa05. * memory stats * Hack up to get per-call numbers * expunge memory counters * buildgen * Automated change: Fix sanity tests * sanity * fix * Automated change: Fix sanity tests * simplify * better * Automated change: Fix sanity tests * Disable windows memory_usage * disable mac * disable mac Co-authored-by: ctiller --- CMakeLists.txt | 37 +- build_autogenerated.yaml | 20 +- gRPC-Core.podspec | 2 - grpc.gyp | 2 - .../compression/message_compress_fuzzer.cc | 3 - .../compression/message_decompress_fuzzer.cc | 3 - test/core/iomgr/stranded_event_test.cc | 1 - test/core/memory_usage/BUILD | 90 +++++ test/core/memory_usage/client.cc | 282 ++++++++++++++++ test/core/memory_usage/memory_usage_test.cc | 95 ++++++ test/core/memory_usage/memstats.h | 31 ++ test/core/memory_usage/server.cc | 318 ++++++++++++++++++ .../transport/chttp2/streams_not_seen_test.cc | 1 - .../transport/chttp2/too_many_pings_test.cc | 1 - .../alts_concurrent_connectivity_test.cc | 1 - test/core/util/BUILD | 2 - test/core/util/fuzzer_corpus_test.cc | 2 - test/core/util/memory_counters.cc | 169 ---------- test/core/util/memory_counters.h | 53 --- test/cpp/microbenchmarks/helpers.cc | 8 - test/cpp/microbenchmarks/helpers.h | 2 - tools/run_tests/generated/tests.json | 20 ++ 22 files changed, 884 insertions(+), 259 deletions(-) create mode 100644 test/core/memory_usage/BUILD create mode 100644 test/core/memory_usage/client.cc create mode 100644 test/core/memory_usage/memory_usage_test.cc create mode 100644 test/core/memory_usage/memstats.h create mode 100644 test/core/memory_usage/server.cc delete mode 100644 test/core/util/memory_counters.cc delete mode 100644 test/core/util/memory_counters.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 68df8560878..eb163b848a1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -700,6 +700,9 @@ if(gRPC_BUILD_TESTS) if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_POSIX) add_dependencies(buildtests_c memory_quota_stress_test) endif() + if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_POSIX) + add_dependencies(buildtests_c memory_usage_test) + endif() add_dependencies(buildtests_c message_compress_test) add_dependencies(buildtests_c minimal_stack_is_minimal_test) add_dependencies(buildtests_c mpmcqueue_test) @@ -2372,7 +2375,6 @@ add_library(grpc_test_util test/core/util/fuzzer_util.cc test/core/util/grpc_profiler.cc test/core/util/histogram.cc - test/core/util/memory_counters.cc test/core/util/mock_endpoint.cc test/core/util/parse_hexstring.cc test/core/util/passthru_endpoint.cc @@ -2441,7 +2443,6 @@ add_library(grpc_test_util_unsecure test/core/util/fuzzer_util.cc test/core/util/grpc_profiler.cc test/core/util/histogram.cc - test/core/util/memory_counters.cc test/core/util/mock_endpoint.cc test/core/util/parse_hexstring.cc test/core/util/passthru_endpoint.cc @@ -6130,6 +6131,37 @@ if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_POSIX) ) +endif() +endif() +if(gRPC_BUILD_TESTS) +if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_POSIX) + + add_executable(memory_usage_test + test/core/memory_usage/memory_usage_test.cc + ) + + target_include_directories(memory_usage_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} + ) + + target_link_libraries(memory_usage_test + ${_gRPC_ALLTARGETS_LIBRARIES} + absl::flags + absl::flags_parse + grpc_test_util + ) + + endif() endif() if(gRPC_BUILD_TESTS) @@ -16936,7 +16968,6 @@ if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX) test/core/util/fuzzer_util.cc test/core/util/grpc_profiler.cc test/core/util/histogram.cc - test/core/util/memory_counters.cc test/core/util/mock_endpoint.cc test/core/util/parse_hexstring.cc test/core/util/passthru_endpoint.cc diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml index e51a8270dfd..cfba2735643 100644 --- a/build_autogenerated.yaml +++ b/build_autogenerated.yaml @@ -1759,7 +1759,6 @@ libs: - test/core/util/fuzzer_util.h - test/core/util/grpc_profiler.h - test/core/util/histogram.h - - test/core/util/memory_counters.h - test/core/util/mock_authorization_endpoint.h - test/core/util/mock_endpoint.h - test/core/util/parse_hexstring.h @@ -1781,7 +1780,6 @@ libs: - test/core/util/fuzzer_util.cc - test/core/util/grpc_profiler.cc - test/core/util/histogram.cc - - test/core/util/memory_counters.cc - test/core/util/mock_endpoint.cc - test/core/util/parse_hexstring.cc - test/core/util/passthru_endpoint.cc @@ -1814,7 +1812,6 @@ libs: - test/core/util/fuzzer_util.h - test/core/util/grpc_profiler.h - test/core/util/histogram.h - - test/core/util/memory_counters.h - test/core/util/mock_authorization_endpoint.h - test/core/util/mock_endpoint.h - test/core/util/parse_hexstring.h @@ -1835,7 +1832,6 @@ libs: - test/core/util/fuzzer_util.cc - test/core/util/grpc_profiler.cc - test/core/util/histogram.cc - - test/core/util/memory_counters.cc - test/core/util/mock_endpoint.cc - test/core/util/parse_hexstring.cc - test/core/util/passthru_endpoint.cc @@ -3940,6 +3936,20 @@ targets: - linux - posix uses_polling: false +- name: memory_usage_test + build: test + language: c + headers: [] + src: + - test/core/memory_usage/memory_usage_test.cc + deps: + - absl/flags:flag + - absl/flags:parse + - grpc_test_util + platforms: + - linux + - posix + uses_polling: false - name: message_compress_test build: test language: c @@ -8493,7 +8503,6 @@ targets: - test/core/util/fuzzer_util.h - test/core/util/grpc_profiler.h - test/core/util/histogram.h - - test/core/util/memory_counters.h - test/core/util/mock_authorization_endpoint.h - test/core/util/mock_endpoint.h - test/core/util/parse_hexstring.h @@ -8517,7 +8526,6 @@ targets: - test/core/util/fuzzer_util.cc - test/core/util/grpc_profiler.cc - test/core/util/histogram.cc - - test/core/util/memory_counters.cc - test/core/util/mock_endpoint.cc - test/core/util/parse_hexstring.cc - test/core/util/passthru_endpoint.cc diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec index 4923a328afa..928a5eb7c32 100644 --- a/gRPC-Core.podspec +++ b/gRPC-Core.podspec @@ -2512,8 +2512,6 @@ Pod::Spec.new do |s| 'test/core/util/grpc_profiler.h', 'test/core/util/histogram.cc', 'test/core/util/histogram.h', - 'test/core/util/memory_counters.cc', - 'test/core/util/memory_counters.h', 'test/core/util/mock_authorization_endpoint.h', 'test/core/util/mock_endpoint.cc', 'test/core/util/mock_endpoint.h', diff --git a/grpc.gyp b/grpc.gyp index dc53358e716..450a471e8d0 100644 --- a/grpc.gyp +++ b/grpc.gyp @@ -1159,7 +1159,6 @@ 'test/core/util/fuzzer_util.cc', 'test/core/util/grpc_profiler.cc', 'test/core/util/histogram.cc', - 'test/core/util/memory_counters.cc', 'test/core/util/mock_endpoint.cc', 'test/core/util/parse_hexstring.cc', 'test/core/util/passthru_endpoint.cc', @@ -1193,7 +1192,6 @@ 'test/core/util/fuzzer_util.cc', 'test/core/util/grpc_profiler.cc', 'test/core/util/histogram.cc', - 'test/core/util/memory_counters.cc', 'test/core/util/mock_endpoint.cc', 'test/core/util/parse_hexstring.cc', 'test/core/util/passthru_endpoint.cc', diff --git a/test/core/compression/message_compress_fuzzer.cc b/test/core/compression/message_compress_fuzzer.cc index f559c1f9a30..58f6489d29b 100644 --- a/test/core/compression/message_compress_fuzzer.cc +++ b/test/core/compression/message_compress_fuzzer.cc @@ -24,10 +24,8 @@ #include "src/core/lib/compression/message_compress.h" #include "src/core/lib/security/credentials/credentials.h" -#include "test/core/util/memory_counters.h" bool squelch = true; -bool leak_check = true; extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { if (size < 1) return 0; @@ -38,7 +36,6 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { const auto compression_algorithm = static_cast(data[0]); - grpc_core::testing::LeakDetector leak_detector(true); grpc_init(); grpc_slice_buffer input_buffer; grpc_slice_buffer_init(&input_buffer); diff --git a/test/core/compression/message_decompress_fuzzer.cc b/test/core/compression/message_decompress_fuzzer.cc index e2d5a96bc02..916ec0cd925 100644 --- a/test/core/compression/message_decompress_fuzzer.cc +++ b/test/core/compression/message_decompress_fuzzer.cc @@ -24,10 +24,8 @@ #include "src/core/lib/compression/message_compress.h" #include "src/core/lib/security/credentials/credentials.h" -#include "test/core/util/memory_counters.h" bool squelch = true; -bool leak_check = true; extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { if (size < 1) return 0; @@ -38,7 +36,6 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { const auto compression_algorithm = static_cast(data[0]); - grpc_core::testing::LeakDetector leak_detector(true); grpc_init(); grpc_slice_buffer input_buffer; grpc_slice_buffer_init(&input_buffer); diff --git a/test/core/iomgr/stranded_event_test.cc b/test/core/iomgr/stranded_event_test.cc index 61fe0294720..320033c1b53 100644 --- a/test/core/iomgr/stranded_event_test.cc +++ b/test/core/iomgr/stranded_event_test.cc @@ -52,7 +52,6 @@ #include "src/core/lib/slice/slice_string_helpers.h" #include "src/core/lib/uri/uri_parser.h" #include "test/core/end2end/cq_verifier.h" -#include "test/core/util/memory_counters.h" #include "test/core/util/port.h" #include "test/core/util/test_config.h" diff --git a/test/core/memory_usage/BUILD b/test/core/memory_usage/BUILD new file mode 100644 index 00000000000..ea6ddfb3c7c --- /dev/null +++ b/test/core/memory_usage/BUILD @@ -0,0 +1,90 @@ +# Copyright 2017 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. + +load("//bazel:grpc_build_system.bzl", "grpc_cc_binary", "grpc_cc_library", "grpc_cc_test", "grpc_package") + +grpc_package(name = "test/core/memory_usage") + +licenses(["notice"]) + +grpc_cc_library( + name = "memstats", + testonly = 1, + hdrs = ["memstats.h"], +) + +grpc_cc_binary( + name = "memory_usage_client", + testonly = 1, + srcs = ["client.cc"], + external_deps = [ + "absl/flags:flag", + "absl/flags:parse", + ], + tags = [ + "no_mac", + "no_windows", + ], + deps = [ + ":memstats", + "//:gpr", + "//:grpc", + "//test/core/util:grpc_test_util", + ], +) + +grpc_cc_binary( + name = "memory_usage_server", + testonly = 1, + srcs = ["server.cc"], + external_deps = [ + "absl/flags:flag", + "absl/flags:parse", + ], + tags = [ + "no_mac", + "no_windows", + ], + deps = [ + ":memstats", + "//:gpr", + "//:grpc", + "//test/core/end2end:ssl_test_data", + "//test/core/util:grpc_test_util", + ], +) + +grpc_cc_test( + name = "memory_usage_test", + srcs = ["memory_usage_test.cc"], + data = [ + ":memory_usage_client", + ":memory_usage_server", + ], + external_deps = [ + "absl/flags:flag", + "absl/flags:parse", + ], + language = "C++", + tags = [ + "no_mac", + "no_windows", + ], + uses_polling = False, + deps = [ + "//:gpr", + "//:grpc", + "//test/core/util:grpc_test_util", + ], +) diff --git a/test/core/memory_usage/client.cc b/test/core/memory_usage/client.cc new file mode 100644 index 00000000000..9734cae13e2 --- /dev/null +++ b/test/core/memory_usage/client.cc @@ -0,0 +1,282 @@ +/* + * + * Copyright 2016 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 +#include + +#include "absl/flags/flag.h" +#include "absl/flags/parse.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "src/core/lib/gpr/env.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gpr/useful.h" +#include "test/core/memory_usage/memstats.h" +#include "test/core/util/test_config.h" + +static grpc_channel* channel; +static grpc_completion_queue* cq; +static grpc_op metadata_ops[2]; +static grpc_op status_ops[2]; +static grpc_op snapshot_ops[6]; +static grpc_op* op; + +typedef struct { + grpc_call* call; + grpc_metadata_array initial_metadata_recv; + grpc_status_code status; + grpc_slice details; + grpc_metadata_array trailing_metadata_recv; +} fling_call; + +// Statically allocate call data structs. Enough to accommodate 100000 ping-pong +// calls and 1 extra for the snapshot calls. +static fling_call calls[100001]; + +static void* tag(intptr_t t) { return reinterpret_cast(t); } + +// A call is intentionally divided into two steps. First step is to initiate a +// call (i.e send and recv metadata). A call is outstanding after we initated, +// so we can measure the call memory usage. +static void init_ping_pong_request(int call_idx) { + grpc_metadata_array_init(&calls[call_idx].initial_metadata_recv); + + memset(metadata_ops, 0, sizeof(metadata_ops)); + op = metadata_ops; + op->op = GRPC_OP_SEND_INITIAL_METADATA; + op->data.send_initial_metadata.count = 0; + op->flags = GRPC_INITIAL_METADATA_WAIT_FOR_READY; + op++; + op->op = GRPC_OP_RECV_INITIAL_METADATA; + op->data.recv_initial_metadata.recv_initial_metadata = + &calls[call_idx].initial_metadata_recv; + op++; + + grpc_slice hostname = grpc_slice_from_static_string("localhost"); + calls[call_idx].call = grpc_channel_create_call( + channel, nullptr, GRPC_PROPAGATE_DEFAULTS, cq, + grpc_slice_from_static_string("/Reflector/reflectUnary"), &hostname, + gpr_inf_future(GPR_CLOCK_REALTIME), nullptr); + + GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(calls[call_idx].call, + metadata_ops, + (size_t)(op - metadata_ops), + tag(call_idx), nullptr)); + grpc_completion_queue_next(cq, gpr_inf_future(GPR_CLOCK_REALTIME), nullptr); +} + +// Second step is to finish the call (i.e recv status) and destroy the call. +static void finish_ping_pong_request(int call_idx) { + grpc_metadata_array_init(&calls[call_idx].trailing_metadata_recv); + + memset(status_ops, 0, sizeof(status_ops)); + op = status_ops; + op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; + op->data.recv_status_on_client.trailing_metadata = + &calls[call_idx].trailing_metadata_recv; + op->data.recv_status_on_client.status = &calls[call_idx].status; + op->data.recv_status_on_client.status_details = &calls[call_idx].details; + op++; + + GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(calls[call_idx].call, + status_ops, + (size_t)(op - status_ops), + tag(call_idx), nullptr)); + grpc_completion_queue_next(cq, gpr_inf_future(GPR_CLOCK_REALTIME), nullptr); + grpc_metadata_array_destroy(&calls[call_idx].initial_metadata_recv); + grpc_metadata_array_destroy(&calls[call_idx].trailing_metadata_recv); + grpc_slice_unref(calls[call_idx].details); + grpc_call_unref(calls[call_idx].call); + calls[call_idx].call = nullptr; +} + +static MemStats send_snapshot_request(int call_idx, grpc_slice call_type) { + grpc_metadata_array_init(&calls[call_idx].initial_metadata_recv); + grpc_metadata_array_init(&calls[call_idx].trailing_metadata_recv); + + grpc_byte_buffer* response_payload_recv = nullptr; + memset(snapshot_ops, 0, sizeof(snapshot_ops)); + op = snapshot_ops; + + op->op = GRPC_OP_SEND_INITIAL_METADATA; + op->data.send_initial_metadata.count = 0; + op->flags = GRPC_INITIAL_METADATA_WAIT_FOR_READY; + op++; + op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; + op++; + op->op = GRPC_OP_RECV_INITIAL_METADATA; + op->data.recv_initial_metadata.recv_initial_metadata = + &calls[call_idx].initial_metadata_recv; + op++; + op->op = GRPC_OP_RECV_MESSAGE; + op->data.recv_message.recv_message = &response_payload_recv; + op++; + op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; + op->data.recv_status_on_client.trailing_metadata = + &calls[call_idx].trailing_metadata_recv; + op->data.recv_status_on_client.status = &calls[call_idx].status; + op->data.recv_status_on_client.status_details = &calls[call_idx].details; + op++; + + grpc_slice hostname = grpc_slice_from_static_string("localhost"); + calls[call_idx].call = grpc_channel_create_call( + channel, nullptr, GRPC_PROPAGATE_DEFAULTS, cq, call_type, &hostname, + gpr_inf_future(GPR_CLOCK_REALTIME), nullptr); + GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(calls[call_idx].call, + snapshot_ops, + (size_t)(op - snapshot_ops), + (void*)nullptr, nullptr)); + grpc_completion_queue_next(cq, gpr_inf_future(GPR_CLOCK_REALTIME), nullptr); + + grpc_byte_buffer_reader reader; + grpc_byte_buffer_reader_init(&reader, response_payload_recv); + grpc_slice response = grpc_byte_buffer_reader_readall(&reader); + MemStats snapshot = + *reinterpret_cast(GRPC_SLICE_START_PTR(response)); + + grpc_metadata_array_destroy(&calls[call_idx].initial_metadata_recv); + grpc_metadata_array_destroy(&calls[call_idx].trailing_metadata_recv); + grpc_slice_unref(response); + grpc_byte_buffer_reader_destroy(&reader); + grpc_byte_buffer_destroy(response_payload_recv); + grpc_slice_unref(calls[call_idx].details); + calls[call_idx].details = grpc_empty_slice(); + grpc_call_unref(calls[call_idx].call); + calls[call_idx].call = nullptr; + + return snapshot; +} + +// Create iterations calls, return MemStats when all outstanding +std::pair run_test_loop(int iterations, int* call_idx) { + grpc_event event; + + // benchmark period + for (int i = 0; i < iterations; ++i) { + init_ping_pong_request(*call_idx + i + 1); + } + + auto peak = std::make_pair( + // client + MemStats::Snapshot(), + // server + send_snapshot_request( + 0, grpc_slice_from_static_string("Reflector/DestroyCalls"))); + + do { + event = grpc_completion_queue_next( + cq, + gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), + gpr_time_from_micros(10000, GPR_TIMESPAN)), + nullptr); + } while (event.type != GRPC_QUEUE_TIMEOUT); + + // second step - recv status and destroy call + for (int i = 0; i < iterations; ++i) { + finish_ping_pong_request(*call_idx + i + 1); + } + + do { + event = grpc_completion_queue_next( + cq, + gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), + gpr_time_from_micros(10000, GPR_TIMESPAN)), + nullptr); + } while (event.type != GRPC_QUEUE_TIMEOUT); + + *call_idx += iterations; + + return peak; +} + +ABSL_FLAG(std::string, target, "localhost:443", "Target host:port"); +ABSL_FLAG(int, warmup, 100, "Warmup iterations"); +ABSL_FLAG(int, benchmark, 1000, "Benchmark iterations"); + +int main(int argc, char** argv) { + absl::ParseCommandLine(argc, argv); + + grpc_slice slice = grpc_slice_from_copied_string("x"); + char* fake_argv[1]; + + GPR_ASSERT(argc >= 1); + fake_argv[0] = argv[0]; + grpc::testing::TestEnvironment env(1, fake_argv); + + grpc_init(); + + for (size_t k = 0; k < GPR_ARRAY_SIZE(calls); k++) { + calls[k].details = grpc_empty_slice(); + } + + cq = grpc_completion_queue_create_for_next(nullptr); + + channel = grpc_channel_create(absl::GetFlag(FLAGS_target).c_str(), + grpc_insecure_credentials_create(), nullptr); + + int call_idx = 0; + const int warmup_iterations = absl::GetFlag(FLAGS_warmup); + const int benchmark_iterations = absl::GetFlag(FLAGS_benchmark); + + // warmup period + MemStats server_benchmark_calls_start = send_snapshot_request( + 0, grpc_slice_from_static_string("Reflector/SimpleSnapshot")); + MemStats client_benchmark_calls_start = MemStats::Snapshot(); + + run_test_loop(warmup_iterations, &call_idx); + + std::pair peak = + run_test_loop(benchmark_iterations, &call_idx); + + MemStats client_calls_inflight = peak.first; + MemStats server_calls_inflight = peak.second; + + grpc_channel_destroy(channel); + grpc_completion_queue_shutdown(cq); + + grpc_event event; + do { + event = grpc_completion_queue_next(cq, gpr_inf_future(GPR_CLOCK_REALTIME), + nullptr); + } while (event.type != GRPC_QUEUE_SHUTDOWN); + grpc_slice_unref(slice); + + grpc_completion_queue_destroy(cq); + grpc_shutdown_blocking(); + + printf("---------client stats--------\n"); + printf("client call memory usage: %f bytes per call\n", + static_cast(client_calls_inflight.rss - + client_benchmark_calls_start.rss) / + benchmark_iterations * 1024); + + printf("---------server stats--------\n"); + printf("server call memory usage: %f bytes per call\n", + static_cast(server_calls_inflight.rss - + server_benchmark_calls_start.rss) / + benchmark_iterations * 1024); + + return 0; +} diff --git a/test/core/memory_usage/memory_usage_test.cc b/test/core/memory_usage/memory_usage_test.cc new file mode 100644 index 00000000000..d535ece9eec --- /dev/null +++ b/test/core/memory_usage/memory_usage_test.cc @@ -0,0 +1,95 @@ +/* + * + * Copyright 2015 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 +#include + +#include "absl/flags/flag.h" +#include "absl/flags/parse.h" +#include "absl/strings/str_cat.h" + +#include +#include +#include + +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gprpp/host_port.h" +#include "test/core/util/port.h" +#include "test/core/util/subprocess.h" + +ABSL_FLAG(int, warmup, 100, "Warmup iterations"); +ABSL_FLAG(int, benchmark, 1000, "Benchmark iterations"); + +class Subprocess { + public: + explicit Subprocess(std::vector args) { + std::vector args_c; + args_c.reserve(args.size()); + for (const auto& arg : args) { + args_c.push_back(arg.c_str()); + } + process_ = gpr_subprocess_create(args_c.size(), args_c.data()); + } + + int Join() { return gpr_subprocess_join(process_); } + void Interrupt() { gpr_subprocess_interrupt(process_); } + + ~Subprocess() { gpr_subprocess_destroy(process_); } + + private: + gpr_subprocess* process_; +}; + +int main(int argc, char** argv) { + absl::ParseCommandLine(argc, argv); + + char* me = argv[0]; + char* lslash = strrchr(me, '/'); + char root[1024]; + int port = grpc_pick_unused_port_or_die(); + std::vector args; + int status; + /* figure out where we are */ + if (lslash) { + memcpy(root, me, static_cast(lslash - me)); + root[lslash - me] = 0; + } else { + strcpy(root, "."); + } + /* start the server */ + Subprocess svr({absl::StrCat(root, "/memory_usage_server", + gpr_subprocess_binary_extension()), + "--bind", grpc_core::JoinHostPort("::", port), "--nosecure"}); + + /* start the client */ + Subprocess cli( + {absl::StrCat(root, "/memory_usage_client", + gpr_subprocess_binary_extension()), + "--target", grpc_core::JoinHostPort("127.0.0.1", port), + absl::StrCat("--warmup=", absl::GetFlag(FLAGS_warmup)), + absl::StrCat("--benchmark=", absl::GetFlag(FLAGS_benchmark))}); + + /* wait for completion */ + if ((status = cli.Join()) != 0) { + printf("client failed with: %d", status); + return 1; + } + + svr.Interrupt(); + return svr.Join() == 0 ? 0 : 2; +} diff --git a/test/core/memory_usage/memstats.h b/test/core/memory_usage/memstats.h new file mode 100644 index 00000000000..615ea17f222 --- /dev/null +++ b/test/core/memory_usage/memstats.h @@ -0,0 +1,31 @@ +// Copyright 2022 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 TEST_H +#define TEST_H + +#include +#include +#include + +struct MemStats { + long rss; // Resident set size, in kb + static MemStats Snapshot() { + struct rusage usage; + if (0 != getrusage(RUSAGE_SELF, &usage)) abort(); + return MemStats{usage.ru_maxrss}; + } +}; + +#endif diff --git a/test/core/memory_usage/server.cc b/test/core/memory_usage/server.cc new file mode 100644 index 00000000000..da1bc165a34 --- /dev/null +++ b/test/core/memory_usage/server.cc @@ -0,0 +1,318 @@ +/* + * + * Copyright 2016 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 +#include +#include +#include +#include + +#include + +#include "absl/flags/flag.h" +#include "absl/flags/parse.h" + +#include +#include + +#include "test/core/memory_usage/memstats.h" +#ifndef _WIN32 +/* This is for _exit() below, which is temporary. */ +#include +#endif + +#include +#include +#include + +#include "src/core/lib/gprpp/host_port.h" +#include "test/core/end2end/data/ssl_test_data.h" +#include "test/core/memory_usage/memstats.h" +#include "test/core/util/port.h" +#include "test/core/util/test_config.h" + +static grpc_completion_queue* cq; +static grpc_server* server; +static grpc_op metadata_ops[2]; +static grpc_op snapshot_ops[5]; +static grpc_op status_op; +static int got_sigint = 0; +static grpc_byte_buffer* payload_buffer = nullptr; +static grpc_byte_buffer* terminal_buffer = nullptr; +static int was_cancelled = 2; + +static void* tag(intptr_t t) { return reinterpret_cast(t); } + +typedef enum { + FLING_SERVER_NEW_REQUEST = 1, + FLING_SERVER_SEND_INIT_METADATA, + FLING_SERVER_WAIT_FOR_DESTROY, + FLING_SERVER_SEND_STATUS_FLING_CALL, + FLING_SERVER_SEND_STATUS_SNAPSHOT, + FLING_SERVER_BATCH_SEND_STATUS_FLING_CALL +} fling_server_tags; + +typedef struct { + fling_server_tags state; + grpc_call* call; + grpc_call_details call_details; + grpc_metadata_array request_metadata_recv; + grpc_metadata_array initial_metadata_send; +} fling_call; + +// hold up to 100000 calls and 6 snaphost calls +static fling_call calls[1000006]; + +static void request_call_unary(int call_idx) { + if (call_idx == static_cast(sizeof(calls) / sizeof(fling_call))) { + gpr_log(GPR_INFO, "Used all call slots (10000) on server. Server exit."); + _exit(0); + } + grpc_metadata_array_init(&calls[call_idx].request_metadata_recv); + grpc_server_request_call( + server, &calls[call_idx].call, &calls[call_idx].call_details, + &calls[call_idx].request_metadata_recv, cq, cq, &calls[call_idx]); +} + +static void send_initial_metadata_unary(void* tag) { + grpc_metadata_array_init( + &(*static_cast(tag)).initial_metadata_send); + metadata_ops[0].op = GRPC_OP_SEND_INITIAL_METADATA; + metadata_ops[0].data.send_initial_metadata.count = 0; + + GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch((*(fling_call*)tag).call, + metadata_ops, 1, tag, + nullptr)); +} + +static void send_status(void* tag) { + status_op.op = GRPC_OP_SEND_STATUS_FROM_SERVER; + status_op.data.send_status_from_server.status = GRPC_STATUS_OK; + status_op.data.send_status_from_server.trailing_metadata_count = 0; + grpc_slice details = grpc_slice_from_static_string(""); + status_op.data.send_status_from_server.status_details = &details; + + GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch((*(fling_call*)tag).call, + &status_op, 1, tag, + nullptr)); +} + +static void send_snapshot(void* tag, MemStats* snapshot) { + grpc_op* op; + + grpc_slice snapshot_slice = + grpc_slice_new(snapshot, sizeof(*snapshot), gpr_free); + payload_buffer = grpc_raw_byte_buffer_create(&snapshot_slice, 1); + grpc_metadata_array_init( + &(*static_cast(tag)).initial_metadata_send); + + op = snapshot_ops; + op->op = GRPC_OP_SEND_INITIAL_METADATA; + op->data.send_initial_metadata.count = 0; + op++; + op->op = GRPC_OP_RECV_MESSAGE; + op->data.recv_message.recv_message = &terminal_buffer; + op++; + op->op = GRPC_OP_SEND_MESSAGE; + if (payload_buffer == nullptr) { + gpr_log(GPR_INFO, "NULL payload buffer !!!"); + } + op->data.send_message.send_message = payload_buffer; + op++; + op->op = GRPC_OP_SEND_STATUS_FROM_SERVER; + op->data.send_status_from_server.status = GRPC_STATUS_OK; + op->data.send_status_from_server.trailing_metadata_count = 0; + grpc_slice details = grpc_slice_from_static_string(""); + op->data.send_status_from_server.status_details = &details; + op++; + op->op = GRPC_OP_RECV_CLOSE_ON_SERVER; + op->data.recv_close_on_server.cancelled = &was_cancelled; + op++; + + GPR_ASSERT(GRPC_CALL_OK == + grpc_call_start_batch((*(fling_call*)tag).call, snapshot_ops, + (size_t)(op - snapshot_ops), tag, nullptr)); +} +/* We have some sort of deadlock, so let's not exit gracefully for now. + When that is resolved, please remove the #include above. */ +static void sigint_handler(int /*x*/) { _exit(0); } + +ABSL_FLAG(std::string, bind, "", "Bind host:port"); +ABSL_FLAG(bool, secure, false, "Use security"); + +int main(int argc, char** argv) { + absl::ParseCommandLine(argc, argv); + + grpc_event ev; + grpc_completion_queue* shutdown_cq; + int shutdown_started = 0; + int shutdown_finished = 0; + + char* fake_argv[1]; + + GPR_ASSERT(argc >= 1); + fake_argv[0] = argv[0]; + grpc::testing::TestEnvironment env(1, fake_argv); + + grpc_init(); + srand(static_cast(clock())); + + std::string addr = absl::GetFlag(FLAGS_bind); + if (addr.empty()) { + addr = grpc_core::JoinHostPort("::", grpc_pick_unused_port_or_die()); + } + gpr_log(GPR_INFO, "creating server on: %s", addr.c_str()); + + cq = grpc_completion_queue_create_for_next(nullptr); + + MemStats before_server_create = MemStats::Snapshot(); + if (absl::GetFlag(FLAGS_secure)) { + grpc_ssl_pem_key_cert_pair pem_key_cert_pair = {test_server1_key, + test_server1_cert}; + grpc_server_credentials* ssl_creds = grpc_ssl_server_credentials_create( + nullptr, &pem_key_cert_pair, 1, 0, nullptr); + server = grpc_server_create(nullptr, nullptr); + GPR_ASSERT(grpc_server_add_http2_port(server, addr.c_str(), ssl_creds)); + grpc_server_credentials_release(ssl_creds); + } else { + server = grpc_server_create(nullptr, nullptr); + GPR_ASSERT(grpc_server_add_http2_port( + server, addr.c_str(), grpc_insecure_server_credentials_create())); + } + + grpc_server_register_completion_queue(server, cq, nullptr); + grpc_server_start(server); + + MemStats after_server_create = MemStats::Snapshot(); + + // initialize call instances + for (int i = 0; i < static_cast(sizeof(calls) / sizeof(fling_call)); + i++) { + grpc_call_details_init(&calls[i].call_details); + calls[i].state = FLING_SERVER_NEW_REQUEST; + } + + int next_call_idx = 0; + MemStats current_snapshot; + + request_call_unary(next_call_idx); + + signal(SIGINT, sigint_handler); + + while (!shutdown_finished) { + if (got_sigint && !shutdown_started) { + gpr_log(GPR_INFO, "Shutting down due to SIGINT"); + + shutdown_cq = grpc_completion_queue_create_for_pluck(nullptr); + grpc_server_shutdown_and_notify(server, shutdown_cq, tag(1000)); + GPR_ASSERT(grpc_completion_queue_pluck( + shutdown_cq, tag(1000), + grpc_timeout_seconds_to_deadline(5), nullptr) + .type == GRPC_OP_COMPLETE); + grpc_completion_queue_destroy(shutdown_cq); + grpc_completion_queue_shutdown(cq); + shutdown_started = 1; + } + ev = grpc_completion_queue_next( + cq, + gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), + gpr_time_from_micros(1000000, GPR_TIMESPAN)), + nullptr); + fling_call* s = static_cast(ev.tag); + switch (ev.type) { + case GRPC_OP_COMPLETE: + switch (s->state) { + case FLING_SERVER_NEW_REQUEST: + request_call_unary(++next_call_idx); + if (0 == grpc_slice_str_cmp(s->call_details.method, + "/Reflector/reflectUnary")) { + s->state = FLING_SERVER_SEND_INIT_METADATA; + send_initial_metadata_unary(s); + } else if (0 == + grpc_slice_str_cmp(s->call_details.method, + "Reflector/GetBeforeSvrCreation")) { + s->state = FLING_SERVER_SEND_STATUS_SNAPSHOT; + send_snapshot(s, &before_server_create); + } else if (0 == + grpc_slice_str_cmp(s->call_details.method, + "Reflector/GetAfterSvrCreation")) { + s->state = FLING_SERVER_SEND_STATUS_SNAPSHOT; + send_snapshot(s, &after_server_create); + } else if (0 == grpc_slice_str_cmp(s->call_details.method, + "Reflector/SimpleSnapshot")) { + s->state = FLING_SERVER_SEND_STATUS_SNAPSHOT; + current_snapshot = MemStats::Snapshot(); + send_snapshot(s, ¤t_snapshot); + } else if (0 == grpc_slice_str_cmp(s->call_details.method, + "Reflector/DestroyCalls")) { + s->state = FLING_SERVER_BATCH_SEND_STATUS_FLING_CALL; + current_snapshot = MemStats::Snapshot(); + send_snapshot(s, ¤t_snapshot); + } else { + gpr_log(GPR_ERROR, "Wrong call method"); + } + break; + case FLING_SERVER_SEND_INIT_METADATA: + s->state = FLING_SERVER_WAIT_FOR_DESTROY; + break; + case FLING_SERVER_WAIT_FOR_DESTROY: + break; + case FLING_SERVER_SEND_STATUS_FLING_CALL: + grpc_call_unref(s->call); + grpc_call_details_destroy(&s->call_details); + grpc_metadata_array_destroy(&s->initial_metadata_send); + grpc_metadata_array_destroy(&s->request_metadata_recv); + break; + case FLING_SERVER_BATCH_SEND_STATUS_FLING_CALL: + for (int k = 0; + k < static_cast(sizeof(calls) / sizeof(fling_call)); + ++k) { + if (calls[k].state == FLING_SERVER_WAIT_FOR_DESTROY) { + calls[k].state = FLING_SERVER_SEND_STATUS_FLING_CALL; + send_status(&calls[k]); + } + } + ABSL_FALLTHROUGH_INTENDED; + // no break here since we want to continue to case + // FLING_SERVER_SEND_STATUS_SNAPSHOT to destroy the snapshot call + case FLING_SERVER_SEND_STATUS_SNAPSHOT: + grpc_byte_buffer_destroy(payload_buffer); + grpc_byte_buffer_destroy(terminal_buffer); + grpc_call_unref(s->call); + grpc_call_details_destroy(&s->call_details); + grpc_metadata_array_destroy(&s->initial_metadata_send); + grpc_metadata_array_destroy(&s->request_metadata_recv); + terminal_buffer = nullptr; + payload_buffer = nullptr; + break; + } + break; + case GRPC_QUEUE_SHUTDOWN: + GPR_ASSERT(shutdown_started); + shutdown_finished = 1; + break; + case GRPC_QUEUE_TIMEOUT: + break; + } + } + + grpc_server_destroy(server); + grpc_completion_queue_destroy(cq); + grpc_shutdown_blocking(); + return 0; +} diff --git a/test/core/transport/chttp2/streams_not_seen_test.cc b/test/core/transport/chttp2/streams_not_seen_test.cc index e6ec6cd35c6..bbd8fc46fd1 100644 --- a/test/core/transport/chttp2/streams_not_seen_test.cc +++ b/test/core/transport/chttp2/streams_not_seen_test.cc @@ -39,7 +39,6 @@ #include "src/core/lib/slice/slice.h" #include "src/core/lib/surface/channel.h" #include "test/core/end2end/cq_verifier.h" -#include "test/core/util/memory_counters.h" #include "test/core/util/port.h" #include "test/core/util/test_config.h" #include "test/core/util/test_tcp_server.h" diff --git a/test/core/transport/chttp2/too_many_pings_test.cc b/test/core/transport/chttp2/too_many_pings_test.cc index 66bf93605a0..f3cc34761c4 100644 --- a/test/core/transport/chttp2/too_many_pings_test.cc +++ b/test/core/transport/chttp2/too_many_pings_test.cc @@ -53,7 +53,6 @@ #include "src/core/lib/slice/slice_string_helpers.h" #include "src/core/lib/surface/channel.h" #include "test/core/end2end/cq_verifier.h" -#include "test/core/util/memory_counters.h" #include "test/core/util/port.h" #include "test/core/util/test_config.h" diff --git a/test/core/tsi/alts/handshaker/alts_concurrent_connectivity_test.cc b/test/core/tsi/alts/handshaker/alts_concurrent_connectivity_test.cc index 7ac7daaae74..eb746c4adde 100644 --- a/test/core/tsi/alts/handshaker/alts_concurrent_connectivity_test.cc +++ b/test/core/tsi/alts/handshaker/alts_concurrent_connectivity_test.cc @@ -57,7 +57,6 @@ #include "test/core/end2end/cq_verifier.h" #include "test/core/tsi/alts/fake_handshaker/fake_handshaker_server.h" #include "test/core/util/fake_udp_and_tcp_server.h" -#include "test/core/util/memory_counters.h" #include "test/core/util/port.h" #include "test/core/util/test_config.h" diff --git a/test/core/util/BUILD b/test/core/util/BUILD index 9d56daaf9ac..14b412d1eef 100644 --- a/test/core/util/BUILD +++ b/test/core/util/BUILD @@ -37,7 +37,6 @@ grpc_cc_library( "fuzzer_util.cc", "grpc_profiler.cc", "histogram.cc", - "memory_counters.cc", "mock_endpoint.cc", "parse_hexstring.cc", "passthru_endpoint.cc", @@ -59,7 +58,6 @@ grpc_cc_library( "fuzzer_util.h", "grpc_profiler.h", "histogram.h", - "memory_counters.h", "mock_authorization_endpoint.h", "mock_endpoint.h", "parse_hexstring.h", diff --git a/test/core/util/fuzzer_corpus_test.cc b/test/core/util/fuzzer_corpus_test.cc index 450609aec35..6433543bce6 100644 --- a/test/core/util/fuzzer_corpus_test.cc +++ b/test/core/util/fuzzer_corpus_test.cc @@ -36,7 +36,6 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size); extern bool squelch; -extern bool leak_check; ABSL_FLAG(std::string, file, "", "Use this file as test data"); ABSL_FLAG(std::string, directory, "", "Use this directory as test data"); @@ -52,7 +51,6 @@ TEST_P(FuzzerCorpusTest, RunOneExample) { gpr_log(GPR_INFO, "Example file: %s", GetParam().c_str()); grpc_slice buffer; squelch = false; - leak_check = false; GPR_ASSERT(GRPC_LOG_IF_ERROR("load_file", grpc_load_file(GetParam().c_str(), 0, &buffer))); size_t length = GRPC_SLICE_LENGTH(buffer); diff --git a/test/core/util/memory_counters.cc b/test/core/util/memory_counters.cc deleted file mode 100644 index 0deb1a4d37c..00000000000 --- a/test/core/util/memory_counters.cc +++ /dev/null @@ -1,169 +0,0 @@ -/* - * - * Copyright 2016 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 "test/core/util/memory_counters.h" - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "src/core/lib/gpr/alloc.h" -#include "src/core/lib/surface/init.h" - -static struct grpc_memory_counters g_memory_counters; -static bool g_memory_counter_enabled; - -#ifdef GPR_LOW_LEVEL_COUNTERS -/* hide these from the microbenchmark atomic stats */ -#define NO_BARRIER_FETCH_ADD(x, sz) \ - __atomic_fetch_add((x), (sz), __ATOMIC_RELAXED) -#define NO_BARRIER_LOAD(x) __atomic_load_n((x), __ATOMIC_RELAXED) -#else -#define NO_BARRIER_FETCH_ADD(x, sz) gpr_atm_no_barrier_fetch_add(x, sz) -#define NO_BARRIER_LOAD(x) gpr_atm_no_barrier_load(x) -#endif - -// Memory counter uses --wrap=symbol feature from ld. To use this, -// `GPR_WRAP_MEMORY_COUNTER` needs to be defined. following options should be -// passed to the compiler. -// -Wl,--wrap=malloc -Wl,--wrap=calloc -Wl,--wrap=realloc -Wl,--wrap=free -// * Reference: https://linux.die.net/man/1/ld) -#if GPR_WRAP_MEMORY_COUNTER - -extern "C" { -void* __real_malloc(size_t size); -void* __real_calloc(size_t size); -void* __real_realloc(void* ptr, size_t size); -void __real_free(void* ptr); - -void* __wrap_malloc(size_t size); -void* __wrap_calloc(size_t size); -void* __wrap_realloc(void* ptr, size_t size); -void __wrap_free(void* ptr); -} - -void* __wrap_malloc(size_t size) { - if (!size) return nullptr; - NO_BARRIER_FETCH_ADD(&g_memory_counters.total_size_absolute, (gpr_atm)size); - NO_BARRIER_FETCH_ADD(&g_memory_counters.total_size_relative, (gpr_atm)size); - NO_BARRIER_FETCH_ADD(&g_memory_counters.total_allocs_absolute, (gpr_atm)1); - NO_BARRIER_FETCH_ADD(&g_memory_counters.total_allocs_relative, (gpr_atm)1); - void* ptr = - __real_malloc(GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(size)) + size); - *static_cast(ptr) = size; - return static_cast(ptr) + GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(size)); -} - -void* __wrap_calloc(size_t size) { - if (!size) return nullptr; - NO_BARRIER_FETCH_ADD(&g_memory_counters.total_size_absolute, (gpr_atm)size); - NO_BARRIER_FETCH_ADD(&g_memory_counters.total_size_relative, (gpr_atm)size); - NO_BARRIER_FETCH_ADD(&g_memory_counters.total_allocs_absolute, (gpr_atm)1); - NO_BARRIER_FETCH_ADD(&g_memory_counters.total_allocs_relative, (gpr_atm)1); - void* ptr = - __real_calloc(GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(size)) + size); - *static_cast(ptr) = size; - return static_cast(ptr) + GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(size)); -} - -void* __wrap_realloc(void* ptr, size_t size) { - if (ptr == nullptr) { - return __wrap_malloc(size); - } - if (size == 0) { - __wrap_free(ptr); - return nullptr; - } - void* rptr = - static_cast(ptr) - GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(size)); - NO_BARRIER_FETCH_ADD(&g_memory_counters.total_size_absolute, (gpr_atm)size); - NO_BARRIER_FETCH_ADD(&g_memory_counters.total_size_relative, - -*static_cast(rptr)); - NO_BARRIER_FETCH_ADD(&g_memory_counters.total_size_relative, (gpr_atm)size); - NO_BARRIER_FETCH_ADD(&g_memory_counters.total_allocs_absolute, (gpr_atm)1); - void* new_ptr = - __real_realloc(rptr, GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(size)) + size); - *static_cast(new_ptr) = size; - return static_cast(new_ptr) + - GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(size)); -} - -void __wrap_free(void* ptr) { - if (ptr == nullptr) return; - void* rptr = - static_cast(ptr) - GPR_ROUND_UP_TO_ALIGNMENT_SIZE(sizeof(size_t)); - NO_BARRIER_FETCH_ADD(&g_memory_counters.total_size_relative, - -*static_cast(rptr)); - NO_BARRIER_FETCH_ADD(&g_memory_counters.total_allocs_relative, -(gpr_atm)1); - __real_free(rptr); -} - -#endif // GPR_WRAP_MEMORY_COUNTER - -void grpc_memory_counters_init() { - memset(&g_memory_counters, 0, sizeof(g_memory_counters)); - g_memory_counter_enabled = true; -} - -void grpc_memory_counters_destroy() { g_memory_counter_enabled = false; } - -struct grpc_memory_counters grpc_memory_counters_snapshot() { - struct grpc_memory_counters counters; - counters.total_size_relative = - NO_BARRIER_LOAD(&g_memory_counters.total_size_relative); - counters.total_size_absolute = - NO_BARRIER_LOAD(&g_memory_counters.total_size_absolute); - counters.total_allocs_relative = - NO_BARRIER_LOAD(&g_memory_counters.total_allocs_relative); - counters.total_allocs_absolute = - NO_BARRIER_LOAD(&g_memory_counters.total_allocs_absolute); - return counters; -} - -namespace grpc_core { -namespace testing { - -LeakDetector::LeakDetector(bool enable) : enabled_(enable) { - if (enabled_) { - grpc_memory_counters_init(); - } -} - -LeakDetector::~LeakDetector() { - // Wait for grpc_shutdown() to finish its async work. - grpc_maybe_wait_for_async_shutdown(); - if (enabled_) { - struct grpc_memory_counters counters = grpc_memory_counters_snapshot(); - if (counters.total_size_relative != 0) { - gpr_log(GPR_ERROR, "Leaking %" PRIuPTR " bytes", - static_cast(counters.total_size_relative)); - GPR_ASSERT(0); - } - grpc_memory_counters_destroy(); - } -} - -} // namespace testing -} // namespace grpc_core diff --git a/test/core/util/memory_counters.h b/test/core/util/memory_counters.h deleted file mode 100644 index c92a001ff13..00000000000 --- a/test/core/util/memory_counters.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * - * Copyright 2016 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_TEST_CORE_UTIL_MEMORY_COUNTERS_H -#define GRPC_TEST_CORE_UTIL_MEMORY_COUNTERS_H - -#include - -struct grpc_memory_counters { - gpr_atm total_size_relative; - gpr_atm total_size_absolute; - gpr_atm total_allocs_relative; - gpr_atm total_allocs_absolute; -}; - -void grpc_memory_counters_init(); -void grpc_memory_counters_destroy(); -struct grpc_memory_counters grpc_memory_counters_snapshot(); - -namespace grpc_core { -namespace testing { - -// At destruction time, it will check there is no memory leak. -// The object should be created before grpc_init() is called and destroyed after -// grpc_shutdown() is returned. -class LeakDetector { - public: - explicit LeakDetector(bool enable); - ~LeakDetector(); - - private: - const bool enabled_; -}; - -} // namespace testing -} // namespace grpc_core - -#endif diff --git a/test/cpp/microbenchmarks/helpers.cc b/test/cpp/microbenchmarks/helpers.cc index d46321c2cce..018b8ccfbbb 100644 --- a/test/cpp/microbenchmarks/helpers.cc +++ b/test/cpp/microbenchmarks/helpers.cc @@ -28,9 +28,6 @@ LibraryInitializer::LibraryInitializer() { g_libraryInitializer = this; g_gli_initializer.summon(); -#ifdef GPR_LOW_LEVEL_COUNTERS - grpc_memory_counters_init(); -#endif init_lib_.init(); } @@ -84,7 +81,6 @@ void TrackCounters::AddToLabel(std::ostream& out, benchmark::State& state) { } #endif #ifdef GPR_LOW_LEVEL_COUNTERS - grpc_memory_counters counters_at_end = grpc_memory_counters_snapshot(); out << " locks/iter:" << ((double)(gpr_atm_no_barrier_load(&gpr_mu_locks) - mu_locks_at_start_) / @@ -100,10 +96,6 @@ void TrackCounters::AddToLabel(std::ostream& out, benchmark::State& state) { << " nows/iter:" << ((double)(gpr_atm_no_barrier_load(&gpr_now_call_count) - now_calls_at_start_) / - (double)state.iterations()) - << " allocs/iter:" - << ((double)(counters_at_end.total_allocs_absolute - - counters_at_start_.total_allocs_absolute) / (double)state.iterations()); #endif } diff --git a/test/cpp/microbenchmarks/helpers.h b/test/cpp/microbenchmarks/helpers.h index f8faa23a49d..6bcee552668 100644 --- a/test/cpp/microbenchmarks/helpers.h +++ b/test/cpp/microbenchmarks/helpers.h @@ -29,7 +29,6 @@ #include #include "src/core/lib/debug/stats.h" -#include "test/core/util/memory_counters.h" class LibraryInitializer { public: @@ -68,7 +67,6 @@ class TrackCounters { gpr_atm_no_barrier_load(&gpr_counter_atm_add); const size_t now_calls_at_start_ = gpr_atm_no_barrier_load(&gpr_now_call_count); - grpc_memory_counters counters_at_start_ = grpc_memory_counters_snapshot(); #endif }; diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json index 0c5f405afaf..5260489e972 100644 --- a/tools/run_tests/generated/tests.json +++ b/tools/run_tests/generated/tests.json @@ -1609,6 +1609,26 @@ ], "uses_polling": false }, + { + "args": [], + "benchmark": false, + "ci_platforms": [ + "linux", + "posix" + ], + "cpu_cost": 1.0, + "exclude_configs": [], + "exclude_iomgrs": [], + "flaky": false, + "gtest": false, + "language": "c", + "name": "memory_usage_test", + "platforms": [ + "linux", + "posix" + ], + "uses_polling": false + }, { "args": [], "benchmark": false,