OLD | NEW |
(Empty) | |
| 1 /* |
| 2 * |
| 3 * Copyright 2015-2016, Google Inc. |
| 4 * All rights reserved. |
| 5 * |
| 6 * Redistribution and use in source and binary forms, with or without |
| 7 * modification, are permitted provided that the following conditions are |
| 8 * met: |
| 9 * |
| 10 * * Redistributions of source code must retain the above copyright |
| 11 * notice, this list of conditions and the following disclaimer. |
| 12 * * Redistributions in binary form must reproduce the above |
| 13 * copyright notice, this list of conditions and the following disclaimer |
| 14 * in the documentation and/or other materials provided with the |
| 15 * distribution. |
| 16 * * Neither the name of Google Inc. nor the names of its |
| 17 * contributors may be used to endorse or promote products derived from |
| 18 * this software without specific prior written permission. |
| 19 * |
| 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 31 * |
| 32 */ |
| 33 |
| 34 #include "src/core/census/mlog.h" |
| 35 #include <grpc/support/cpu.h> |
| 36 #include <grpc/support/log.h> |
| 37 #include <grpc/support/port_platform.h> |
| 38 #include <grpc/support/sync.h> |
| 39 #include <grpc/support/thd.h> |
| 40 #include <grpc/support/time.h> |
| 41 #include <grpc/support/useful.h> |
| 42 #include <stdio.h> |
| 43 #include <stdlib.h> |
| 44 #include <string.h> |
| 45 #include "test/core/util/test_config.h" |
| 46 |
| 47 // Change this to non-zero if you want more output. |
| 48 #define VERBOSE 0 |
| 49 |
| 50 // Log size to use for all tests. |
| 51 #define LOG_SIZE_IN_MB 1 |
| 52 #define LOG_SIZE_IN_BYTES (LOG_SIZE_IN_MB << 20) |
| 53 |
| 54 // Fills in 'record' of size 'size'. Each byte in record is filled in with the |
| 55 // same value. The value is extracted from 'record' pointer. |
| 56 static void write_record(char* record, size_t size) { |
| 57 char data = (char)((uintptr_t)record % 255); |
| 58 memset(record, data, size); |
| 59 } |
| 60 |
| 61 // Reads fixed size records. Returns the number of records read in |
| 62 // 'num_records'. |
| 63 static void read_records(size_t record_size, const char* buffer, |
| 64 size_t buffer_size, int* num_records) { |
| 65 GPR_ASSERT(buffer_size >= record_size); |
| 66 GPR_ASSERT(buffer_size % record_size == 0); |
| 67 *num_records = (int)(buffer_size / record_size); |
| 68 for (int i = 0; i < *num_records; ++i) { |
| 69 const char* record = buffer + (record_size * (size_t)i); |
| 70 char data = (char)((uintptr_t)record % 255); |
| 71 for (size_t j = 0; j < record_size; ++j) { |
| 72 GPR_ASSERT(data == record[j]); |
| 73 } |
| 74 } |
| 75 } |
| 76 |
| 77 // Tries to write the specified number of records. Stops when the log gets |
| 78 // full. Returns the number of records written. Spins for random |
| 79 // number of times, up to 'max_spin_count', between writes. |
| 80 static int write_records_to_log(int writer_id, size_t record_size, |
| 81 int num_records, int max_spin_count) { |
| 82 int counter = 0; |
| 83 for (int i = 0; i < num_records; ++i) { |
| 84 int spin_count = max_spin_count ? rand() % max_spin_count : 0; |
| 85 if (VERBOSE && (counter++ == num_records / 10)) { |
| 86 printf(" Writer %d: %d out of %d written\n", writer_id, i, num_records); |
| 87 counter = 0; |
| 88 } |
| 89 char* record = (char*)(census_log_start_write(record_size)); |
| 90 if (record == NULL) { |
| 91 return i; |
| 92 } |
| 93 write_record(record, record_size); |
| 94 census_log_end_write(record, record_size); |
| 95 for (int j = 0; j < spin_count; ++j) { |
| 96 GPR_ASSERT(j >= 0); |
| 97 } |
| 98 } |
| 99 return num_records; |
| 100 } |
| 101 |
| 102 // Performs a single read iteration. Returns the number of records read. |
| 103 static int perform_read_iteration(size_t record_size) { |
| 104 const void* read_buffer = NULL; |
| 105 size_t bytes_available; |
| 106 int records_read = 0; |
| 107 census_log_init_reader(); |
| 108 while ((read_buffer = census_log_read_next(&bytes_available))) { |
| 109 int num_records = 0; |
| 110 read_records(record_size, (const char*)read_buffer, bytes_available, |
| 111 &num_records); |
| 112 records_read += num_records; |
| 113 } |
| 114 return records_read; |
| 115 } |
| 116 |
| 117 // Asserts that the log is empty. |
| 118 static void assert_log_empty(void) { |
| 119 census_log_init_reader(); |
| 120 size_t bytes_available; |
| 121 GPR_ASSERT(census_log_read_next(&bytes_available) == NULL); |
| 122 } |
| 123 |
| 124 // Fills the log and verifies data. If 'no fragmentation' is true, records |
| 125 // are sized such that CENSUS_LOG_2_MAX_RECORD_SIZE is a multiple of record |
| 126 // size. If not a circular log, verifies that the number of records written |
| 127 // match the number of records read. |
| 128 static void fill_log(size_t log_size, int no_fragmentation, int circular_log) { |
| 129 size_t size; |
| 130 if (no_fragmentation) { |
| 131 int log2size = rand() % (CENSUS_LOG_2_MAX_RECORD_SIZE + 1); |
| 132 size = ((size_t)1 << log2size); |
| 133 } else { |
| 134 while (1) { |
| 135 size = 1 + ((size_t)rand() % CENSUS_LOG_MAX_RECORD_SIZE); |
| 136 if (CENSUS_LOG_MAX_RECORD_SIZE % size) { |
| 137 break; |
| 138 } |
| 139 } |
| 140 } |
| 141 int records_written = |
| 142 write_records_to_log(0 /* writer id */, size, |
| 143 (int)((log_size / size) * 2), 0 /* spin count */); |
| 144 int records_read = perform_read_iteration(size); |
| 145 if (!circular_log) { |
| 146 GPR_ASSERT(records_written == records_read); |
| 147 } |
| 148 assert_log_empty(); |
| 149 } |
| 150 |
| 151 // Structure to pass args to writer_thread |
| 152 typedef struct writer_thread_args { |
| 153 // Index of this thread in the writers vector. |
| 154 int index; |
| 155 // Record size. |
| 156 size_t record_size; |
| 157 // Number of records to write. |
| 158 int num_records; |
| 159 // Used to signal when writer is complete |
| 160 gpr_cv* done; |
| 161 gpr_mu* mu; |
| 162 int* count; |
| 163 } writer_thread_args; |
| 164 |
| 165 // Writes the given number of records of random size (up to kMaxRecordSize) and |
| 166 // random data to the specified log. |
| 167 static void writer_thread(void* arg) { |
| 168 writer_thread_args* args = (writer_thread_args*)arg; |
| 169 // Maximum number of times to spin between writes. |
| 170 static const int MAX_SPIN_COUNT = 50; |
| 171 int records_written = 0; |
| 172 if (VERBOSE) { |
| 173 printf(" Writer %d starting\n", args->index); |
| 174 } |
| 175 while (records_written < args->num_records) { |
| 176 records_written += write_records_to_log(args->index, args->record_size, |
| 177 args->num_records - records_written, |
| 178 MAX_SPIN_COUNT); |
| 179 if (records_written < args->num_records) { |
| 180 // Ran out of log space. Sleep for a bit and let the reader catch up. |
| 181 // This should never happen for circular logs. |
| 182 if (VERBOSE) { |
| 183 printf( |
| 184 " Writer %d stalled due to out-of-space: %d out of %d " |
| 185 "written\n", |
| 186 args->index, records_written, args->num_records); |
| 187 } |
| 188 gpr_sleep_until(GRPC_TIMEOUT_MILLIS_TO_DEADLINE(10)); |
| 189 } |
| 190 } |
| 191 // Done. Decrement count and signal. |
| 192 gpr_mu_lock(args->mu); |
| 193 (*args->count)--; |
| 194 gpr_cv_signal(args->done); |
| 195 if (VERBOSE) { |
| 196 printf(" Writer %d done\n", args->index); |
| 197 } |
| 198 gpr_mu_unlock(args->mu); |
| 199 } |
| 200 |
| 201 // struct to pass args to reader_thread |
| 202 typedef struct reader_thread_args { |
| 203 // Record size. |
| 204 size_t record_size; |
| 205 // Interval between read iterations. |
| 206 int read_iteration_interval_in_msec; |
| 207 // Total number of records. |
| 208 int total_records; |
| 209 // Signalled when reader should stop. |
| 210 gpr_cv stop; |
| 211 int stop_flag; |
| 212 // Used to signal when reader has finished |
| 213 gpr_cv* done; |
| 214 gpr_mu* mu; |
| 215 int running; |
| 216 } reader_thread_args; |
| 217 |
| 218 // Reads and verifies the specified number of records. Reader can also be |
| 219 // stopped via gpr_cv_signal(&args->stop). Sleeps for 'read_interval_in_msec' |
| 220 // between read iterations. |
| 221 static void reader_thread(void* arg) { |
| 222 reader_thread_args* args = (reader_thread_args*)arg; |
| 223 if (VERBOSE) { |
| 224 printf(" Reader starting\n"); |
| 225 } |
| 226 gpr_timespec interval = gpr_time_from_micros( |
| 227 args->read_iteration_interval_in_msec * 1000, GPR_TIMESPAN); |
| 228 gpr_mu_lock(args->mu); |
| 229 int records_read = 0; |
| 230 int num_iterations = 0; |
| 231 int counter = 0; |
| 232 while (!args->stop_flag && records_read < args->total_records) { |
| 233 gpr_cv_wait(&args->stop, args->mu, interval); |
| 234 if (!args->stop_flag) { |
| 235 records_read += perform_read_iteration(args->record_size); |
| 236 GPR_ASSERT(records_read <= args->total_records); |
| 237 if (VERBOSE && (counter++ == 100000)) { |
| 238 printf(" Reader: %d out of %d read\n", records_read, |
| 239 args->total_records); |
| 240 counter = 0; |
| 241 } |
| 242 ++num_iterations; |
| 243 } |
| 244 } |
| 245 // Done |
| 246 args->running = 0; |
| 247 gpr_cv_signal(args->done); |
| 248 if (VERBOSE) { |
| 249 printf(" Reader: records: %d, iterations: %d\n", records_read, |
| 250 num_iterations); |
| 251 } |
| 252 gpr_mu_unlock(args->mu); |
| 253 } |
| 254 |
| 255 // Creates NUM_WRITERS writers where each writer writes NUM_RECORDS_PER_WRITER |
| 256 // records. Also, starts a reader that iterates over and reads blocks every |
| 257 // READ_ITERATION_INTERVAL_IN_MSEC. |
| 258 // Number of writers. |
| 259 #define NUM_WRITERS 5 |
| 260 static void multiple_writers_single_reader(int circular_log) { |
| 261 // Sleep interval between read iterations. |
| 262 static const int READ_ITERATION_INTERVAL_IN_MSEC = 10; |
| 263 // Maximum record size. |
| 264 static const size_t MAX_RECORD_SIZE = 20; |
| 265 // Number of records written by each writer. This is sized such that we |
| 266 // will write through the entire log ~10 times. |
| 267 const int NUM_RECORDS_PER_WRITER = |
| 268 (int)((10 * census_log_remaining_space()) / (MAX_RECORD_SIZE / 2)) / |
| 269 NUM_WRITERS; |
| 270 size_t record_size = ((size_t)rand() % MAX_RECORD_SIZE) + 1; |
| 271 // Create and start writers. |
| 272 writer_thread_args writers[NUM_WRITERS]; |
| 273 int writers_count = NUM_WRITERS; |
| 274 gpr_cv writers_done; |
| 275 gpr_mu writers_mu; // protects writers_done and writers_count |
| 276 gpr_cv_init(&writers_done); |
| 277 gpr_mu_init(&writers_mu); |
| 278 gpr_thd_id id; |
| 279 for (int i = 0; i < NUM_WRITERS; ++i) { |
| 280 writers[i].index = i; |
| 281 writers[i].record_size = record_size; |
| 282 writers[i].num_records = NUM_RECORDS_PER_WRITER; |
| 283 writers[i].done = &writers_done; |
| 284 writers[i].count = &writers_count; |
| 285 writers[i].mu = &writers_mu; |
| 286 gpr_thd_new(&id, &writer_thread, &writers[i], NULL); |
| 287 } |
| 288 // Start reader. |
| 289 gpr_cv reader_done; |
| 290 gpr_mu reader_mu; // protects reader_done and reader.running |
| 291 reader_thread_args reader; |
| 292 reader.record_size = record_size; |
| 293 reader.read_iteration_interval_in_msec = READ_ITERATION_INTERVAL_IN_MSEC; |
| 294 reader.total_records = NUM_WRITERS * NUM_RECORDS_PER_WRITER; |
| 295 reader.stop_flag = 0; |
| 296 gpr_cv_init(&reader.stop); |
| 297 gpr_cv_init(&reader_done); |
| 298 reader.done = &reader_done; |
| 299 gpr_mu_init(&reader_mu); |
| 300 reader.mu = &reader_mu; |
| 301 reader.running = 1; |
| 302 gpr_thd_new(&id, &reader_thread, &reader, NULL); |
| 303 // Wait for writers to finish. |
| 304 gpr_mu_lock(&writers_mu); |
| 305 while (writers_count != 0) { |
| 306 gpr_cv_wait(&writers_done, &writers_mu, gpr_inf_future(GPR_CLOCK_REALTIME)); |
| 307 } |
| 308 gpr_mu_unlock(&writers_mu); |
| 309 gpr_mu_destroy(&writers_mu); |
| 310 gpr_cv_destroy(&writers_done); |
| 311 gpr_mu_lock(&reader_mu); |
| 312 if (circular_log) { |
| 313 // Stop reader. |
| 314 reader.stop_flag = 1; |
| 315 gpr_cv_signal(&reader.stop); |
| 316 } |
| 317 // wait for reader to finish |
| 318 while (reader.running) { |
| 319 gpr_cv_wait(&reader_done, &reader_mu, gpr_inf_future(GPR_CLOCK_REALTIME)); |
| 320 } |
| 321 if (circular_log) { |
| 322 // Assert that there were no out-of-space errors. |
| 323 GPR_ASSERT(0 == census_log_out_of_space_count()); |
| 324 } |
| 325 gpr_mu_unlock(&reader_mu); |
| 326 gpr_mu_destroy(&reader_mu); |
| 327 gpr_cv_destroy(&reader_done); |
| 328 if (VERBOSE) { |
| 329 printf(" Reader: finished\n"); |
| 330 } |
| 331 } |
| 332 |
| 333 static void setup_test(int circular_log) { |
| 334 census_log_initialize(LOG_SIZE_IN_MB, circular_log); |
| 335 GPR_ASSERT(census_log_remaining_space() == LOG_SIZE_IN_BYTES); |
| 336 } |
| 337 |
| 338 // Attempts to create a record of invalid size (size > |
| 339 // CENSUS_LOG_MAX_RECORD_SIZE). |
| 340 void test_invalid_record_size(void) { |
| 341 static const size_t INVALID_SIZE = CENSUS_LOG_MAX_RECORD_SIZE + 1; |
| 342 static const size_t VALID_SIZE = 1; |
| 343 printf("Starting test: invalid record size\n"); |
| 344 setup_test(0); |
| 345 void* record = census_log_start_write(INVALID_SIZE); |
| 346 GPR_ASSERT(record == NULL); |
| 347 // Now try writing a valid record. |
| 348 record = census_log_start_write(VALID_SIZE); |
| 349 GPR_ASSERT(record != NULL); |
| 350 census_log_end_write(record, VALID_SIZE); |
| 351 // Verifies that available space went down by one block. In theory, this |
| 352 // check can fail if the thread is context switched to a new CPU during the |
| 353 // start_write execution (multiple blocks get allocated), but this has not |
| 354 // been observed in practice. |
| 355 GPR_ASSERT(LOG_SIZE_IN_BYTES - CENSUS_LOG_MAX_RECORD_SIZE == |
| 356 census_log_remaining_space()); |
| 357 census_log_shutdown(); |
| 358 } |
| 359 |
| 360 // Tests end_write() with a different size than what was specified in |
| 361 // start_write(). |
| 362 void test_end_write_with_different_size(void) { |
| 363 static const size_t START_WRITE_SIZE = 10; |
| 364 static const size_t END_WRITE_SIZE = 7; |
| 365 printf("Starting test: end write with different size\n"); |
| 366 setup_test(0); |
| 367 void* record_written = census_log_start_write(START_WRITE_SIZE); |
| 368 GPR_ASSERT(record_written != NULL); |
| 369 census_log_end_write(record_written, END_WRITE_SIZE); |
| 370 census_log_init_reader(); |
| 371 size_t bytes_available; |
| 372 const void* record_read = census_log_read_next(&bytes_available); |
| 373 GPR_ASSERT(record_written == record_read); |
| 374 GPR_ASSERT(END_WRITE_SIZE == bytes_available); |
| 375 assert_log_empty(); |
| 376 census_log_shutdown(); |
| 377 } |
| 378 |
| 379 // Verifies that pending records are not available via read_next(). |
| 380 void test_read_pending_record(void) { |
| 381 static const size_t PR_RECORD_SIZE = 1024; |
| 382 printf("Starting test: read pending record\n"); |
| 383 setup_test(0); |
| 384 // Start a write. |
| 385 void* record_written = census_log_start_write(PR_RECORD_SIZE); |
| 386 GPR_ASSERT(record_written != NULL); |
| 387 // As write is pending, read should fail. |
| 388 census_log_init_reader(); |
| 389 size_t bytes_available; |
| 390 const void* record_read = census_log_read_next(&bytes_available); |
| 391 GPR_ASSERT(record_read == NULL); |
| 392 // A read followed by end_write() should succeed. |
| 393 census_log_end_write(record_written, PR_RECORD_SIZE); |
| 394 census_log_init_reader(); |
| 395 record_read = census_log_read_next(&bytes_available); |
| 396 GPR_ASSERT(record_written == record_read); |
| 397 GPR_ASSERT(PR_RECORD_SIZE == bytes_available); |
| 398 assert_log_empty(); |
| 399 census_log_shutdown(); |
| 400 } |
| 401 |
| 402 // Tries reading beyond pending write. |
| 403 void test_read_beyond_pending_record(void) { |
| 404 printf("Starting test: read beyond pending record\n"); |
| 405 setup_test(0); |
| 406 // Start a write. |
| 407 const size_t incomplete_record_size = 10; |
| 408 void* incomplete_record = census_log_start_write(incomplete_record_size); |
| 409 GPR_ASSERT(incomplete_record != NULL); |
| 410 const size_t complete_record_size = 20; |
| 411 void* complete_record = census_log_start_write(complete_record_size); |
| 412 GPR_ASSERT(complete_record != NULL); |
| 413 GPR_ASSERT(complete_record != incomplete_record); |
| 414 census_log_end_write(complete_record, complete_record_size); |
| 415 // Now iterate over blocks to read completed records. |
| 416 census_log_init_reader(); |
| 417 size_t bytes_available; |
| 418 const void* record_read = census_log_read_next(&bytes_available); |
| 419 GPR_ASSERT(complete_record == record_read); |
| 420 GPR_ASSERT(complete_record_size == bytes_available); |
| 421 // Complete first record. |
| 422 census_log_end_write(incomplete_record, incomplete_record_size); |
| 423 // Have read past the incomplete record, so read_next() should return NULL. |
| 424 // NB: this test also assumes our thread did not get switched to a different |
| 425 // CPU between the two start_write calls |
| 426 record_read = census_log_read_next(&bytes_available); |
| 427 GPR_ASSERT(record_read == NULL); |
| 428 // Reset reader to get the newly completed record. |
| 429 census_log_init_reader(); |
| 430 record_read = census_log_read_next(&bytes_available); |
| 431 GPR_ASSERT(incomplete_record == record_read); |
| 432 GPR_ASSERT(incomplete_record_size == bytes_available); |
| 433 assert_log_empty(); |
| 434 census_log_shutdown(); |
| 435 } |
| 436 |
| 437 // Tests scenario where block being read is detached from a core and put on the |
| 438 // dirty list. |
| 439 void test_detached_while_reading(void) { |
| 440 printf("Starting test: detached while reading\n"); |
| 441 setup_test(0); |
| 442 // Start a write. |
| 443 static const size_t DWR_RECORD_SIZE = 10; |
| 444 void* record_written = census_log_start_write(DWR_RECORD_SIZE); |
| 445 GPR_ASSERT(record_written != NULL); |
| 446 census_log_end_write(record_written, DWR_RECORD_SIZE); |
| 447 // Read this record. |
| 448 census_log_init_reader(); |
| 449 size_t bytes_available; |
| 450 const void* record_read = census_log_read_next(&bytes_available); |
| 451 GPR_ASSERT(record_read != NULL); |
| 452 GPR_ASSERT(DWR_RECORD_SIZE == bytes_available); |
| 453 // Now fill the log. This will move the block being read from core-local |
| 454 // array to the dirty list. |
| 455 while ((record_written = census_log_start_write(DWR_RECORD_SIZE))) { |
| 456 census_log_end_write(record_written, DWR_RECORD_SIZE); |
| 457 } |
| 458 |
| 459 // In this iteration, read_next() should only traverse blocks in the |
| 460 // core-local array. Therefore, we expect at most gpr_cpu_num_cores() more |
| 461 // blocks. As log is full, if read_next() is traversing the dirty list, we |
| 462 // will get more than gpr_cpu_num_cores() blocks. |
| 463 int block_read = 0; |
| 464 while ((record_read = census_log_read_next(&bytes_available))) { |
| 465 ++block_read; |
| 466 GPR_ASSERT(block_read <= (int)gpr_cpu_num_cores()); |
| 467 } |
| 468 census_log_shutdown(); |
| 469 } |
| 470 |
| 471 // Fills non-circular log with records sized such that size is a multiple of |
| 472 // CENSUS_LOG_MAX_RECORD_SIZE (no per-block fragmentation). |
| 473 void test_fill_log_no_fragmentation(void) { |
| 474 printf("Starting test: fill log no fragmentation\n"); |
| 475 const int circular = 0; |
| 476 setup_test(circular); |
| 477 fill_log(LOG_SIZE_IN_BYTES, 1 /* no fragmentation */, circular); |
| 478 census_log_shutdown(); |
| 479 } |
| 480 |
| 481 // Fills circular log with records sized such that size is a multiple of |
| 482 // CENSUS_LOG_MAX_RECORD_SIZE (no per-block fragmentation). |
| 483 void test_fill_circular_log_no_fragmentation(void) { |
| 484 printf("Starting test: fill circular log no fragmentation\n"); |
| 485 const int circular = 1; |
| 486 setup_test(circular); |
| 487 fill_log(LOG_SIZE_IN_BYTES, 1 /* no fragmentation */, circular); |
| 488 census_log_shutdown(); |
| 489 } |
| 490 |
| 491 // Fills non-circular log with records that may straddle end of a block. |
| 492 void test_fill_log_with_straddling_records(void) { |
| 493 printf("Starting test: fill log with straddling records\n"); |
| 494 const int circular = 0; |
| 495 setup_test(circular); |
| 496 fill_log(LOG_SIZE_IN_BYTES, 0 /* block straddling records */, circular); |
| 497 census_log_shutdown(); |
| 498 } |
| 499 |
| 500 // Fills circular log with records that may straddle end of a block. |
| 501 void test_fill_circular_log_with_straddling_records(void) { |
| 502 printf("Starting test: fill circular log with straddling records\n"); |
| 503 const int circular = 1; |
| 504 setup_test(circular); |
| 505 fill_log(LOG_SIZE_IN_BYTES, 0 /* block straddling records */, circular); |
| 506 census_log_shutdown(); |
| 507 } |
| 508 |
| 509 // Tests scenario where multiple writers and a single reader are using a log |
| 510 // that is configured to discard old records. |
| 511 void test_multiple_writers_circular_log(void) { |
| 512 printf("Starting test: multiple writers circular log\n"); |
| 513 const int circular = 1; |
| 514 setup_test(circular); |
| 515 multiple_writers_single_reader(circular); |
| 516 census_log_shutdown(); |
| 517 } |
| 518 |
| 519 // Tests scenario where multiple writers and a single reader are using a log |
| 520 // that is configured to discard old records. |
| 521 void test_multiple_writers(void) { |
| 522 printf("Starting test: multiple writers\n"); |
| 523 const int circular = 0; |
| 524 setup_test(circular); |
| 525 multiple_writers_single_reader(circular); |
| 526 census_log_shutdown(); |
| 527 } |
| 528 |
| 529 // Repeat the straddling records and multiple writers tests with a small log. |
| 530 void test_small_log(void) { |
| 531 printf("Starting test: small log\n"); |
| 532 const int circular = 0; |
| 533 census_log_initialize(0, circular); |
| 534 size_t log_size = census_log_remaining_space(); |
| 535 GPR_ASSERT(log_size > 0); |
| 536 fill_log(log_size, 0, circular); |
| 537 census_log_shutdown(); |
| 538 census_log_initialize(0, circular); |
| 539 multiple_writers_single_reader(circular); |
| 540 census_log_shutdown(); |
| 541 } |
| 542 |
| 543 void test_performance(void) { |
| 544 for (size_t write_size = 1; write_size < CENSUS_LOG_MAX_RECORD_SIZE; |
| 545 write_size *= 2) { |
| 546 setup_test(0); |
| 547 gpr_timespec start_time = gpr_now(GPR_CLOCK_REALTIME); |
| 548 int nrecords = 0; |
| 549 while (1) { |
| 550 void* record = census_log_start_write(write_size); |
| 551 if (record == NULL) { |
| 552 break; |
| 553 } |
| 554 census_log_end_write(record, write_size); |
| 555 nrecords++; |
| 556 } |
| 557 gpr_timespec write_time = |
| 558 gpr_time_sub(gpr_now(GPR_CLOCK_REALTIME), start_time); |
| 559 double write_time_micro = |
| 560 (double)write_time.tv_sec * 1000000 + (double)write_time.tv_nsec / 1000; |
| 561 census_log_shutdown(); |
| 562 printf( |
| 563 "Wrote %d %d byte records in %.3g microseconds: %g records/us " |
| 564 "(%g ns/record), %g gigabytes/s\n", |
| 565 nrecords, (int)write_size, write_time_micro, |
| 566 nrecords / write_time_micro, 1000 * write_time_micro / nrecords, |
| 567 (double)((int)write_size * nrecords) / write_time_micro / 1000); |
| 568 } |
| 569 } |
| 570 |
| 571 int main(int argc, char** argv) { |
| 572 grpc_test_init(argc, argv); |
| 573 gpr_time_init(); |
| 574 srand((unsigned)gpr_now(GPR_CLOCK_REALTIME).tv_nsec); |
| 575 test_invalid_record_size(); |
| 576 test_end_write_with_different_size(); |
| 577 test_read_pending_record(); |
| 578 test_read_beyond_pending_record(); |
| 579 test_detached_while_reading(); |
| 580 test_fill_log_no_fragmentation(); |
| 581 test_fill_circular_log_no_fragmentation(); |
| 582 test_fill_log_with_straddling_records(); |
| 583 test_fill_circular_log_with_straddling_records(); |
| 584 test_small_log(); |
| 585 test_multiple_writers(); |
| 586 test_multiple_writers_circular_log(); |
| 587 test_performance(); |
| 588 return 0; |
| 589 } |
OLD | NEW |