diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/core/end2end/end2end_nosec_tests.c | 8 | ||||
-rw-r--r-- | test/core/end2end/end2end_tests.c | 8 | ||||
-rwxr-xr-x | test/core/end2end/gen_build_yaml.py | 1 | ||||
-rw-r--r-- | test/core/end2end/tests/authority_not_supported.c | 193 | ||||
-rw-r--r-- | test/cpp/qps/driver.cc | 15 | ||||
-rw-r--r-- | test/cpp/qps/qps_json_driver.cc | 142 | ||||
-rw-r--r-- | test/cpp/qps/report.cc | 15 | ||||
-rw-r--r-- | test/cpp/qps/report.h | 6 | ||||
-rw-r--r-- | test/cpp/qps/server.h | 2 | ||||
-rw-r--r-- | test/cpp/qps/usage_timer.cc | 33 | ||||
-rw-r--r-- | test/cpp/qps/usage_timer.h | 2 |
11 files changed, 399 insertions, 26 deletions
diff --git a/test/core/end2end/end2end_nosec_tests.c b/test/core/end2end/end2end_nosec_tests.c index caaa97c215..457c188ce5 100644 --- a/test/core/end2end/end2end_nosec_tests.c +++ b/test/core/end2end/end2end_nosec_tests.c @@ -43,6 +43,8 @@ static bool g_pre_init_called = false; +extern void authority_not_supported(grpc_end2end_test_config config); +extern void authority_not_supported_pre_init(void); extern void bad_hostname(grpc_end2end_test_config config); extern void bad_hostname_pre_init(void); extern void binary_metadata(grpc_end2end_test_config config); @@ -135,6 +137,7 @@ extern void trailing_metadata_pre_init(void); void grpc_end2end_tests_pre_init(void) { GPR_ASSERT(!g_pre_init_called); g_pre_init_called = true; + authority_not_supported_pre_init(); bad_hostname_pre_init(); binary_metadata_pre_init(); cancel_after_accept_pre_init(); @@ -188,6 +191,7 @@ void grpc_end2end_tests(int argc, char **argv, GPR_ASSERT(g_pre_init_called); if (argc <= 1) { + authority_not_supported(config); bad_hostname(config); binary_metadata(config); cancel_after_accept(config); @@ -236,6 +240,10 @@ void grpc_end2end_tests(int argc, char **argv, } for (i = 1; i < argc; i++) { + if (0 == strcmp("authority_not_supported", argv[i])) { + authority_not_supported(config); + continue; + } if (0 == strcmp("bad_hostname", argv[i])) { bad_hostname(config); continue; diff --git a/test/core/end2end/end2end_tests.c b/test/core/end2end/end2end_tests.c index 6d17e686e6..dc2d68a486 100644 --- a/test/core/end2end/end2end_tests.c +++ b/test/core/end2end/end2end_tests.c @@ -43,6 +43,8 @@ static bool g_pre_init_called = false; +extern void authority_not_supported(grpc_end2end_test_config config); +extern void authority_not_supported_pre_init(void); extern void bad_hostname(grpc_end2end_test_config config); extern void bad_hostname_pre_init(void); extern void binary_metadata(grpc_end2end_test_config config); @@ -137,6 +139,7 @@ extern void trailing_metadata_pre_init(void); void grpc_end2end_tests_pre_init(void) { GPR_ASSERT(!g_pre_init_called); g_pre_init_called = true; + authority_not_supported_pre_init(); bad_hostname_pre_init(); binary_metadata_pre_init(); call_creds_pre_init(); @@ -191,6 +194,7 @@ void grpc_end2end_tests(int argc, char **argv, GPR_ASSERT(g_pre_init_called); if (argc <= 1) { + authority_not_supported(config); bad_hostname(config); binary_metadata(config); call_creds(config); @@ -240,6 +244,10 @@ void grpc_end2end_tests(int argc, char **argv, } for (i = 1; i < argc; i++) { + if (0 == strcmp("authority_not_supported", argv[i])) { + authority_not_supported(config); + continue; + } if (0 == strcmp("bad_hostname", argv[i])) { bad_hostname(config); continue; diff --git a/test/core/end2end/gen_build_yaml.py b/test/core/end2end/gen_build_yaml.py index 26eee93439..5f54b821a7 100755 --- a/test/core/end2end/gen_build_yaml.py +++ b/test/core/end2end/gen_build_yaml.py @@ -141,6 +141,7 @@ END2END_TESTS = { 'simple_request': default_test_options, 'streaming_error_response': default_test_options, 'trailing_metadata': default_test_options, + 'authority_not_supported': default_test_options, } diff --git a/test/core/end2end/tests/authority_not_supported.c b/test/core/end2end/tests/authority_not_supported.c new file mode 100644 index 0000000000..0b49dc8584 --- /dev/null +++ b/test/core/end2end/tests/authority_not_supported.c @@ -0,0 +1,193 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "test/core/end2end/end2end_tests.h" + +#include <stdio.h> +#include <string.h> + +#include <grpc/byte_buffer.h> +#include <grpc/support/alloc.h> +#include <grpc/support/log.h> +#include <grpc/support/time.h> +#include <grpc/support/useful.h> +#include "test/core/end2end/cq_verifier.h" + +static void *tag(intptr_t t) { return (void *)t; } + +static grpc_end2end_test_fixture begin_test(grpc_end2end_test_config config, + const char *test_name, + grpc_channel_args *client_args, + grpc_channel_args *server_args) { + grpc_end2end_test_fixture f; + gpr_log(GPR_INFO, "%s/%s", test_name, config.name); + f = config.create_fixture(client_args, server_args); + config.init_server(&f, server_args); + config.init_client(&f, client_args); + return f; +} + +static gpr_timespec n_seconds_time(int n) { + return GRPC_TIMEOUT_SECONDS_TO_DEADLINE(n); +} + +static gpr_timespec five_seconds_time(void) { return n_seconds_time(5); } + +static void drain_cq(grpc_completion_queue *cq) { + grpc_event ev; + do { + ev = grpc_completion_queue_next(cq, five_seconds_time(), NULL); + } while (ev.type != GRPC_QUEUE_SHUTDOWN); +} + +static void shutdown_server(grpc_end2end_test_fixture *f) { + if (!f->server) return; + grpc_server_shutdown_and_notify(f->server, f->cq, tag(1000)); + GPR_ASSERT(grpc_completion_queue_pluck( + f->cq, tag(1000), GRPC_TIMEOUT_SECONDS_TO_DEADLINE(5), NULL) + .type == GRPC_OP_COMPLETE); + grpc_server_destroy(f->server); + f->server = NULL; +} + +static void shutdown_client(grpc_end2end_test_fixture *f) { + if (!f->client) return; + grpc_channel_destroy(f->client); + f->client = NULL; +} + +static void end_test(grpc_end2end_test_fixture *f) { + shutdown_server(f); + shutdown_client(f); + + grpc_completion_queue_shutdown(f->cq); + drain_cq(f->cq); + grpc_completion_queue_destroy(f->cq); +} + +/* Request/response with metadata and payload.*/ +static void test_with_authority_header(grpc_end2end_test_config config) { + grpc_call *c; + grpc_slice request_payload_slice = grpc_slice_from_copied_string("hello world"); + grpc_byte_buffer *request_payload = + grpc_raw_byte_buffer_create(&request_payload_slice, 1); + gpr_timespec deadline = five_seconds_time(); + grpc_metadata meta_c[2] = { + {"key1", "val1", 4, 0, {{NULL, NULL, NULL, NULL}}}, + {"key2", "val2", 4, 0, {{NULL, NULL, NULL, NULL}}}}; + grpc_end2end_test_fixture f = + begin_test(config, "test_with_authority_header", NULL, NULL); + cq_verifier *cqv = cq_verifier_create(f.cq); + grpc_op ops[6]; + grpc_op *op; + grpc_metadata_array initial_metadata_recv; + grpc_metadata_array trailing_metadata_recv; + grpc_byte_buffer *response_payload_recv = NULL; + grpc_status_code status; + grpc_call_error error; + char *details = NULL; + size_t details_capacity = 0; + + c = grpc_channel_create_call(f.client, NULL, GRPC_PROPAGATE_DEFAULTS, f.cq, + "/foo", "foo.test.google.fr", deadline, NULL); + GPR_ASSERT(c); + + grpc_metadata_array_init(&initial_metadata_recv); + grpc_metadata_array_init(&trailing_metadata_recv); + + memset(ops, 0, sizeof(ops)); + op = ops; + op->op = GRPC_OP_SEND_INITIAL_METADATA; + op->data.send_initial_metadata.count = 2; + op->data.send_initial_metadata.metadata = meta_c; + op->flags = 0; + op->reserved = NULL; + op++; + op->op = GRPC_OP_SEND_MESSAGE; + op->data.send_message = request_payload; + op->flags = 0; + op->reserved = NULL; + op++; + op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT; + op->flags = 0; + op->reserved = NULL; + op++; + op->op = GRPC_OP_RECV_INITIAL_METADATA; + op->data.recv_initial_metadata = &initial_metadata_recv; + op->flags = 0; + op->reserved = NULL; + op++; + op->op = GRPC_OP_RECV_MESSAGE; + op->data.recv_message = &response_payload_recv; + op->flags = 0; + op->reserved = NULL; + op++; + op->op = GRPC_OP_RECV_STATUS_ON_CLIENT; + op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv; + op->data.recv_status_on_client.status = &status; + op->data.recv_status_on_client.status_details = &details; + op->data.recv_status_on_client.status_details_capacity = &details_capacity; + op->flags = 0; + op->reserved = NULL; + op++; + error = grpc_call_start_batch(c, ops, (size_t)(op - ops), tag(1), NULL); + GPR_ASSERT(GRPC_CALL_OK == error); + + CQ_EXPECT_COMPLETION(cqv, tag(1), 1); + cq_verify(cqv); + + GPR_ASSERT(status == GRPC_STATUS_CANCELLED); + + gpr_free(details); + grpc_metadata_array_destroy(&initial_metadata_recv); + grpc_metadata_array_destroy(&trailing_metadata_recv); + + grpc_call_destroy(c); + + cq_verifier_destroy(cqv); + + grpc_byte_buffer_destroy(request_payload); + grpc_byte_buffer_destroy(response_payload_recv); + + end_test(&f); + config.tear_down_data(&f); +} + +void authority_not_supported(grpc_end2end_test_config config) { + if (config.feature_mask & FEATURE_MASK_SUPPORTS_AUTHORITY_HEADER) { + return; + } + test_with_authority_header(config); +} + +void authority_not_supported_pre_init(void) {} diff --git a/test/cpp/qps/driver.cc b/test/cpp/qps/driver.cc index a440341ccf..cf3372c3cf 100644 --- a/test/cpp/qps/driver.cc +++ b/test/cpp/qps/driver.cc @@ -125,6 +125,8 @@ static double UserTime(ClientStats s) { return s.time_user(); } static double ServerWallTime(ServerStats s) { return s.time_elapsed(); } static double ServerSystemTime(ServerStats s) { return s.time_system(); } static double ServerUserTime(ServerStats s) { return s.time_user(); } +static double ServerTotalCpuTime(ServerStats s) { return s.total_cpu_time(); } +static double ServerIdleCpuTime(ServerStats s) { return s.idle_cpu_time(); } static int Cores(int n) { return n; } // Postprocess ScenarioResult and populate result summary. @@ -149,6 +151,7 @@ static void postprocess_scenario_result(ScenarioResult* result) { sum(result->server_stats(), ServerWallTime); auto server_user_time = 100.0 * sum(result->server_stats(), ServerUserTime) / sum(result->server_stats(), ServerWallTime); + auto client_system_time = 100.0 * sum(result->client_stats(), SystemTime) / sum(result->client_stats(), WallTime); auto client_user_time = 100.0 * sum(result->client_stats(), UserTime) / @@ -159,6 +162,18 @@ static void postprocess_scenario_result(ScenarioResult* result) { result->mutable_summary()->set_client_system_time(client_system_time); result->mutable_summary()->set_client_user_time(client_user_time); + // For Non-linux platform, get_cpu_usage() is not implemented. Thus, + // ServerTotalCpuTime and ServerIdleCpuTime are both 0. + if (average(result->server_stats(), ServerTotalCpuTime) == 0) { + result->mutable_summary()->set_server_cpu_usage(0); + } else { + auto server_cpu_usage = + 100 - + 100 * average(result->server_stats(), ServerIdleCpuTime) / + average(result->server_stats(), ServerTotalCpuTime); + result->mutable_summary()->set_server_cpu_usage(server_cpu_usage); + } + if (result->request_results_size() > 0) { int64_t successes = 0; int64_t failures = 0; diff --git a/test/cpp/qps/qps_json_driver.cc b/test/cpp/qps/qps_json_driver.cc index 1524ebbc38..31b5917fb7 100644 --- a/test/cpp/qps/qps_json_driver.cc +++ b/test/cpp/qps/qps_json_driver.cc @@ -49,10 +49,111 @@ DEFINE_string(scenarios_file, "", DEFINE_string(scenarios_json, "", "JSON string containing an array of Scenario objects"); DEFINE_bool(quit, false, "Quit the workers"); +DEFINE_string(search_param, "", + "The parameter, whose value is to be searched for to achieve " + "targeted cpu load. For now, we have 'offered_load'. Later, " + "'num_channels', 'num_outstanding_requests', etc. shall be " + "added."); +DEFINE_double( + initial_search_value, 0.0, + "initial parameter value to start the search with (i.e. lower bound)"); +DEFINE_double(targeted_cpu_load, 70.0, + "Targeted cpu load (unit: %, range [0,100])"); +DEFINE_double(stride, 1, + "Defines each stride of the search. The larger the stride is, " + "the coarser the result will be, but will also be faster."); +DEFINE_double(error_tolerance, 0.01, + "Defines threshold for stopping the search. When current search " + "range is narrower than the error_tolerance computed range, we " + "stop the search."); namespace grpc { namespace testing { +static std::unique_ptr<ScenarioResult> RunAndReport(const Scenario& scenario, + bool* success) { + std::cerr << "RUNNING SCENARIO: " << scenario.name() << "\n"; + auto result = + RunScenario(scenario.client_config(), scenario.num_clients(), + scenario.server_config(), scenario.num_servers(), + scenario.warmup_seconds(), scenario.benchmark_seconds(), + scenario.spawn_local_worker_count()); + + // Amend the result with scenario config. Eventually we should adjust + // RunScenario contract so we don't need to touch the result here. + result->mutable_scenario()->CopyFrom(scenario); + + GetReporter()->ReportQPS(*result); + GetReporter()->ReportQPSPerCore(*result); + GetReporter()->ReportLatency(*result); + GetReporter()->ReportTimes(*result); + GetReporter()->ReportCpuUsage(*result); + + for (int i = 0; *success && i < result->client_success_size(); i++) { + *success = result->client_success(i); + } + for (int i = 0; *success && i < result->server_success_size(); i++) { + *success = result->server_success(i); + } + + return result; +} + +static double GetCpuLoad(Scenario* scenario, double offered_load, + bool* success) { + scenario->mutable_client_config() + ->mutable_load_params() + ->mutable_poisson() + ->set_offered_load(offered_load); + auto result = RunAndReport(*scenario, success); + return result->summary().server_cpu_usage(); +} + +static double BinarySearch(Scenario* scenario, double targeted_cpu_load, + double low, double high, bool* success) { + while (low <= high * (1 - FLAGS_error_tolerance)) { + double mid = low + (high - low) / 2; + double current_cpu_load = GetCpuLoad(scenario, mid, success); + gpr_log(GPR_DEBUG, "Binary Search: current_offered_load %.0f", mid); + if (!*success) { + gpr_log(GPR_ERROR, "Client/Server Failure"); + break; + } + if (targeted_cpu_load <= current_cpu_load) { + high = mid - FLAGS_stride; + } else { + low = mid + FLAGS_stride; + } + } + + return low; +} + +static double SearchOfferedLoad(double initial_offered_load, + double targeted_cpu_load, Scenario* scenario, + bool* success) { + std::cerr << "RUNNING SCENARIO: " << scenario->name() << "\n"; + double current_offered_load = initial_offered_load; + double current_cpu_load = GetCpuLoad(scenario, current_offered_load, success); + if (current_cpu_load > targeted_cpu_load) { + gpr_log(GPR_ERROR, "Initial offered load too high"); + return -1; + } + + while (*success && (current_cpu_load < targeted_cpu_load)) { + current_offered_load *= 2; + current_cpu_load = GetCpuLoad(scenario, current_offered_load, success); + gpr_log(GPR_DEBUG, "Binary Search: current_offered_load %.0f", + current_offered_load); + } + + double targeted_offered_load = + BinarySearch(scenario, targeted_cpu_load, current_offered_load / 2, + current_offered_load, success); + + return targeted_offered_load; +} + static bool QpsDriver() { grpc::string json; @@ -68,11 +169,11 @@ static bool QpsDriver() { if (scfile) { // Read the json data from disk - FILE *json_file = fopen(FLAGS_scenarios_file.c_str(), "r"); + FILE* json_file = fopen(FLAGS_scenarios_file.c_str(), "r"); GPR_ASSERT(json_file != NULL); fseek(json_file, 0, SEEK_END); long len = ftell(json_file); - char *data = new char[len]; + char* data = new char[len]; fseek(json_file, 0, SEEK_SET); GPR_ASSERT(len == (long)fread(data, 1, len, json_file)); fclose(json_file); @@ -93,28 +194,19 @@ static bool QpsDriver() { GPR_ASSERT(scenarios.scenarios_size() > 0); for (int i = 0; i < scenarios.scenarios_size(); i++) { - const Scenario &scenario = scenarios.scenarios(i); - std::cerr << "RUNNING SCENARIO: " << scenario.name() << "\n"; - auto result = - RunScenario(scenario.client_config(), scenario.num_clients(), - scenario.server_config(), scenario.num_servers(), - scenario.warmup_seconds(), scenario.benchmark_seconds(), - scenario.spawn_local_worker_count()); - - // Amend the result with scenario config. Eventually we should adjust - // RunScenario contract so we don't need to touch the result here. - result->mutable_scenario()->CopyFrom(scenario); - - GetReporter()->ReportQPS(*result); - GetReporter()->ReportQPSPerCore(*result); - GetReporter()->ReportLatency(*result); - GetReporter()->ReportTimes(*result); - - for (int i = 0; success && i < result->client_success_size(); i++) { - success = result->client_success(i); - } - for (int i = 0; success && i < result->server_success_size(); i++) { - success = result->server_success(i); + if (FLAGS_search_param == "") { + const Scenario& scenario = scenarios.scenarios(i); + RunAndReport(scenario, &success); + } else { + if (FLAGS_search_param == "offered_load") { + Scenario* scenario = scenarios.mutable_scenarios(i); + double targeted_offered_load = + SearchOfferedLoad(FLAGS_initial_search_value, + FLAGS_targeted_cpu_load, scenario, &success); + gpr_log(GPR_INFO, "targeted_offered_load %f", targeted_offered_load); + } else { + gpr_log(GPR_ERROR, "Unimplemented search param"); + } } } return success; @@ -123,7 +215,7 @@ static bool QpsDriver() { } // namespace testing } // namespace grpc -int main(int argc, char **argv) { +int main(int argc, char** argv) { grpc::testing::InitBenchmark(&argc, &argv, true); bool ok = grpc::testing::QpsDriver(); diff --git a/test/cpp/qps/report.cc b/test/cpp/qps/report.cc index 41617e968a..7f84816421 100644 --- a/test/cpp/qps/report.cc +++ b/test/cpp/qps/report.cc @@ -71,6 +71,12 @@ void CompositeReporter::ReportTimes(const ScenarioResult& result) { } } +void CompositeReporter::ReportCpuUsage(const ScenarioResult& result) { + for (size_t i = 0; i < reporters_.size(); ++i) { + reporters_[i]->ReportCpuUsage(result); + } +} + void GprLogReporter::ReportQPS(const ScenarioResult& result) { gpr_log(GPR_INFO, "QPS: %.1f", result.summary().qps()); if (result.summary().failed_requests_per_second() > 0) { @@ -107,6 +113,11 @@ void GprLogReporter::ReportTimes(const ScenarioResult& result) { result.summary().client_user_time()); } +void GprLogReporter::ReportCpuUsage(const ScenarioResult& result) { + gpr_log(GPR_INFO, "Server CPU usage: %.2f%%", + result.summary().server_cpu_usage()); +} + void JsonReporter::ReportQPS(const ScenarioResult& result) { grpc::string json_string = SerializeJson(result, "type.googleapis.com/grpc.testing.ScenarioResult"); @@ -127,5 +138,9 @@ void JsonReporter::ReportTimes(const ScenarioResult& result) { // NOP - all reporting is handled by ReportQPS. } +void JsonReporter::ReportCpuUsage(const ScenarioResult& result) { + // NOP - all reporting is handled by ReportQPS. +} + } // namespace testing } // namespace grpc diff --git a/test/cpp/qps/report.h b/test/cpp/qps/report.h index 9dc259e95a..faf87ff060 100644 --- a/test/cpp/qps/report.h +++ b/test/cpp/qps/report.h @@ -70,6 +70,9 @@ class Reporter { /** Reports system and user time for client and server systems. */ virtual void ReportTimes(const ScenarioResult& result) = 0; + /** Reports server cpu usage. */ + virtual void ReportCpuUsage(const ScenarioResult& result) = 0; + private: const string name_; }; @@ -86,6 +89,7 @@ class CompositeReporter : public Reporter { void ReportQPSPerCore(const ScenarioResult& result) override; void ReportLatency(const ScenarioResult& result) override; void ReportTimes(const ScenarioResult& result) override; + void ReportCpuUsage(const ScenarioResult& result) override; private: std::vector<std::unique_ptr<Reporter> > reporters_; @@ -101,6 +105,7 @@ class GprLogReporter : public Reporter { void ReportQPSPerCore(const ScenarioResult& result) override; void ReportLatency(const ScenarioResult& result) override; void ReportTimes(const ScenarioResult& result) override; + void ReportCpuUsage(const ScenarioResult& result) override; }; /** Dumps the report to a JSON file. */ @@ -114,6 +119,7 @@ class JsonReporter : public Reporter { void ReportQPSPerCore(const ScenarioResult& result) override; void ReportLatency(const ScenarioResult& result) override; void ReportTimes(const ScenarioResult& result) override; + void ReportCpuUsage(const ScenarioResult& result) override; const string report_file_; }; diff --git a/test/cpp/qps/server.h b/test/cpp/qps/server.h index e8bc396696..c3d18e5789 100644 --- a/test/cpp/qps/server.h +++ b/test/cpp/qps/server.h @@ -75,6 +75,8 @@ class Server { stats.set_time_elapsed(timer_result.wall); stats.set_time_system(timer_result.system); stats.set_time_user(timer_result.user); + stats.set_total_cpu_time(timer_result.total_cpu_time); + stats.set_idle_cpu_time(timer_result.idle_cpu_time); return stats; } diff --git a/test/cpp/qps/usage_timer.cc b/test/cpp/qps/usage_timer.cc index ff595b2ba0..c6697fbdfd 100644 --- a/test/cpp/qps/usage_timer.cc +++ b/test/cpp/qps/usage_timer.cc @@ -33,10 +33,14 @@ #include "test/cpp/qps/usage_timer.h" +#include <fstream> +#include <sstream> +#include <string> + +#include <grpc/support/log.h> #include <grpc/support/time.h> #include <sys/resource.h> #include <sys/time.h> - UsageTimer::UsageTimer() : start_(Sample()) {} double UsageTimer::Now() { @@ -48,6 +52,27 @@ static double time_double(struct timeval* tv) { return tv->tv_sec + 1e-6 * tv->tv_usec; } +static void get_cpu_usage(unsigned long long* total_cpu_time, + unsigned long long* idle_cpu_time) { +#ifdef __linux__ + std::ifstream proc_stat("/proc/stat"); + proc_stat.ignore(5); + std::string cpu_time_str; + std::string first_line; + std::getline(proc_stat, first_line); + std::stringstream first_line_s(first_line); + for (int i = 0; i < 10; ++i) { + std::getline(first_line_s, cpu_time_str, ' '); + *total_cpu_time += std::stol(cpu_time_str); + if (i == 3) { + *idle_cpu_time = std::stol(cpu_time_str); + } + } +#else + gpr_log(GPR_INFO, "get_cpu_usage(): Non-linux platform is not supported."); +#endif +} + UsageTimer::Result UsageTimer::Sample() { struct rusage usage; struct timeval tv; @@ -58,6 +83,9 @@ UsageTimer::Result UsageTimer::Sample() { r.wall = time_double(&tv); r.user = time_double(&usage.ru_utime); r.system = time_double(&usage.ru_stime); + r.total_cpu_time = 0; + r.idle_cpu_time = 0; + get_cpu_usage(&r.total_cpu_time, &r.idle_cpu_time); return r; } @@ -67,5 +95,8 @@ UsageTimer::Result UsageTimer::Mark() const { r.wall = s.wall - start_.wall; r.user = s.user - start_.user; r.system = s.system - start_.system; + r.total_cpu_time = s.total_cpu_time - start_.total_cpu_time; + r.idle_cpu_time = s.idle_cpu_time - start_.idle_cpu_time; + return r; } diff --git a/test/cpp/qps/usage_timer.h b/test/cpp/qps/usage_timer.h index 8343cd6653..0fc1b47996 100644 --- a/test/cpp/qps/usage_timer.h +++ b/test/cpp/qps/usage_timer.h @@ -42,6 +42,8 @@ class UsageTimer { double wall; double user; double system; + unsigned long long total_cpu_time; + unsigned long long idle_cpu_time; }; Result Mark() const; |