blob: a1fadc2290a10c948634bab9524fabb748e57347 [file] [log] [blame]
Alistair Veitch85afe712016-02-02 17:58:15 -08001/*
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
Craig Tiller9533d042016-03-25 17:11:06 -070034#include "src/core/lib/census/mlog.h"
Alistair Veitch85afe712016-02-02 17:58:15 -080035#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
Alistair Veitch58a61852016-02-11 13:31:05 -080047// Change this to non-zero if you want more output.
Alistair Veitch85afe712016-02-02 17:58:15 -080048#define VERBOSE 0
49
Alistair Veitch58a61852016-02-11 13:31:05 -080050// Log size to use for all tests.
Alistair Veitch85afe712016-02-02 17:58:15 -080051#define LOG_SIZE_IN_MB 1
52#define LOG_SIZE_IN_BYTES (LOG_SIZE_IN_MB << 20)
53
Alistair Veitch58a61852016-02-11 13:31:05 -080054// 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.
Alistair Veitch85afe712016-02-02 17:58:15 -080056static void write_record(char* record, size_t size) {
57 char data = (char)((uintptr_t)record % 255);
58 memset(record, data, size);
59}
60
Alistair Veitch58a61852016-02-11 13:31:05 -080061// Reads fixed size records. Returns the number of records read in
62// 'num_records'.
Alistair Veitch85afe712016-02-02 17:58:15 -080063static 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
Alistair Veitch58a61852016-02-11 13:31:05 -080077// 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.
Alistair Veitch85afe712016-02-02 17:58:15 -080080static 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
Alistair Veitch58a61852016-02-11 13:31:05 -0800102// Performs a single read iteration. Returns the number of records read.
Alistair Veitch85afe712016-02-02 17:58:15 -0800103static 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
Alistair Veitch58a61852016-02-11 13:31:05 -0800117// Asserts that the log is empty.
Alistair Veitch85afe712016-02-02 17:58:15 -0800118static 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
Alistair Veitch58a61852016-02-11 13:31:05 -0800124// 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.
Alistair Veitch85afe712016-02-02 17:58:15 -0800128static 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
Alistair Veitch58a61852016-02-11 13:31:05 -0800151// Structure to pass args to writer_thread
Alistair Veitch85afe712016-02-02 17:58:15 -0800152typedef struct writer_thread_args {
Alistair Veitch58a61852016-02-11 13:31:05 -0800153 // Index of this thread in the writers vector.
Alistair Veitch85afe712016-02-02 17:58:15 -0800154 int index;
Alistair Veitch58a61852016-02-11 13:31:05 -0800155 // Record size.
Alistair Veitch85afe712016-02-02 17:58:15 -0800156 size_t record_size;
Alistair Veitch58a61852016-02-11 13:31:05 -0800157 // Number of records to write.
Alistair Veitch85afe712016-02-02 17:58:15 -0800158 int num_records;
Alistair Veitch58a61852016-02-11 13:31:05 -0800159 // Used to signal when writer is complete
Alistair Veitch85afe712016-02-02 17:58:15 -0800160 gpr_cv* done;
161 gpr_mu* mu;
162 int* count;
163} writer_thread_args;
164
Alistair Veitch58a61852016-02-11 13:31:05 -0800165// Writes the given number of records of random size (up to kMaxRecordSize) and
166// random data to the specified log.
Alistair Veitch85afe712016-02-02 17:58:15 -0800167static void writer_thread(void* arg) {
168 writer_thread_args* args = (writer_thread_args*)arg;
Alistair Veitch58a61852016-02-11 13:31:05 -0800169 // Maximum number of times to spin between writes.
Alistair Veitch85afe712016-02-02 17:58:15 -0800170 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) {
Alistair Veitch58a61852016-02-11 13:31:05 -0800180 // Ran out of log space. Sleep for a bit and let the reader catch up.
181 // This should never happen for circular logs.
Alistair Veitch85afe712016-02-02 17:58:15 -0800182 if (VERBOSE) {
Alistair Veitch49653c52016-02-05 11:59:29 -0800183 printf(
Alistair Veitch58a61852016-02-11 13:31:05 -0800184 " Writer %d stalled due to out-of-space: %d out of %d "
185 "written\n",
Alistair Veitch49653c52016-02-05 11:59:29 -0800186 args->index, records_written, args->num_records);
Alistair Veitch85afe712016-02-02 17:58:15 -0800187 }
188 gpr_sleep_until(GRPC_TIMEOUT_MILLIS_TO_DEADLINE(10));
189 }
190 }
Alistair Veitch58a61852016-02-11 13:31:05 -0800191 // Done. Decrement count and signal.
Alistair Veitch85afe712016-02-02 17:58:15 -0800192 gpr_mu_lock(args->mu);
193 (*args->count)--;
Alistair Veitch49653c52016-02-05 11:59:29 -0800194 gpr_cv_signal(args->done);
Alistair Veitch85afe712016-02-02 17:58:15 -0800195 if (VERBOSE) {
196 printf(" Writer %d done\n", args->index);
197 }
198 gpr_mu_unlock(args->mu);
199}
200
Alistair Veitch58a61852016-02-11 13:31:05 -0800201// struct to pass args to reader_thread
Alistair Veitch85afe712016-02-02 17:58:15 -0800202typedef struct reader_thread_args {
Alistair Veitch58a61852016-02-11 13:31:05 -0800203 // Record size.
Alistair Veitch85afe712016-02-02 17:58:15 -0800204 size_t record_size;
Alistair Veitch58a61852016-02-11 13:31:05 -0800205 // Interval between read iterations.
Alistair Veitch85afe712016-02-02 17:58:15 -0800206 int read_iteration_interval_in_msec;
Alistair Veitch58a61852016-02-11 13:31:05 -0800207 // Total number of records.
Alistair Veitch85afe712016-02-02 17:58:15 -0800208 int total_records;
Alistair Veitch58a61852016-02-11 13:31:05 -0800209 // Signalled when reader should stop.
Alistair Veitch85afe712016-02-02 17:58:15 -0800210 gpr_cv stop;
211 int stop_flag;
Alistair Veitch58a61852016-02-11 13:31:05 -0800212 // Used to signal when reader has finished
Alistair Veitch85afe712016-02-02 17:58:15 -0800213 gpr_cv* done;
214 gpr_mu* mu;
215 int running;
216} reader_thread_args;
217
Alistair Veitch58a61852016-02-11 13:31:05 -0800218// 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.
Alistair Veitch85afe712016-02-02 17:58:15 -0800221static 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 }
Alistair Veitch58a61852016-02-11 13:31:05 -0800245 // Done
Alistair Veitch85afe712016-02-02 17:58:15 -0800246 args->running = 0;
Alistair Veitch49653c52016-02-05 11:59:29 -0800247 gpr_cv_signal(args->done);
Alistair Veitch85afe712016-02-02 17:58:15 -0800248 if (VERBOSE) {
249 printf(" Reader: records: %d, iterations: %d\n", records_read,
250 num_iterations);
251 }
252 gpr_mu_unlock(args->mu);
253}
254
Alistair Veitch58a61852016-02-11 13:31:05 -0800255// 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.
Alistair Veitch85afe712016-02-02 17:58:15 -0800259#define NUM_WRITERS 5
260static void multiple_writers_single_reader(int circular_log) {
Alistair Veitch58a61852016-02-11 13:31:05 -0800261 // Sleep interval between read iterations.
Alistair Veitch85afe712016-02-02 17:58:15 -0800262 static const int READ_ITERATION_INTERVAL_IN_MSEC = 10;
Alistair Veitch58a61852016-02-11 13:31:05 -0800263 // Maximum record size.
Alistair Veitch85afe712016-02-02 17:58:15 -0800264 static const size_t MAX_RECORD_SIZE = 20;
Alistair Veitch58a61852016-02-11 13:31:05 -0800265 // Number of records written by each writer. This is sized such that we
266 // will write through the entire log ~10 times.
Alistair Veitch85afe712016-02-02 17:58:15 -0800267 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;
Alistair Veitch58a61852016-02-11 13:31:05 -0800271 // Create and start writers.
Alistair Veitch85afe712016-02-02 17:58:15 -0800272 writer_thread_args writers[NUM_WRITERS];
273 int writers_count = NUM_WRITERS;
274 gpr_cv writers_done;
Alistair Veitch58a61852016-02-11 13:31:05 -0800275 gpr_mu writers_mu; // protects writers_done and writers_count
Alistair Veitch85afe712016-02-02 17:58:15 -0800276 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 }
Alistair Veitch58a61852016-02-11 13:31:05 -0800288 // Start reader.
Alistair Veitch85afe712016-02-02 17:58:15 -0800289 gpr_cv reader_done;
Alistair Veitch58a61852016-02-11 13:31:05 -0800290 gpr_mu reader_mu; // protects reader_done and reader.running
Alistair Veitch85afe712016-02-02 17:58:15 -0800291 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);
Alistair Veitch58a61852016-02-11 13:31:05 -0800303 // Wait for writers to finish.
Alistair Veitch85afe712016-02-02 17:58:15 -0800304 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) {
Alistair Veitch58a61852016-02-11 13:31:05 -0800313 // Stop reader.
Alistair Veitch85afe712016-02-02 17:58:15 -0800314 reader.stop_flag = 1;
315 gpr_cv_signal(&reader.stop);
316 }
Alistair Veitch58a61852016-02-11 13:31:05 -0800317 // wait for reader to finish
Alistair Veitch85afe712016-02-02 17:58:15 -0800318 while (reader.running) {
319 gpr_cv_wait(&reader_done, &reader_mu, gpr_inf_future(GPR_CLOCK_REALTIME));
320 }
321 if (circular_log) {
Alistair Veitch58a61852016-02-11 13:31:05 -0800322 // Assert that there were no out-of-space errors.
Alistair Veitch85afe712016-02-02 17:58:15 -0800323 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
333static void setup_test(int circular_log) {
334 census_log_initialize(LOG_SIZE_IN_MB, circular_log);
Nicolas "Pixel" Noble1faa0362016-03-15 21:28:09 +0100335 // GPR_ASSERT(census_log_remaining_space() == LOG_SIZE_IN_BYTES);
Alistair Veitch85afe712016-02-02 17:58:15 -0800336}
337
Alistair Veitch58a61852016-02-11 13:31:05 -0800338// Attempts to create a record of invalid size (size >
339// CENSUS_LOG_MAX_RECORD_SIZE).
Alistair Veitch85afe712016-02-02 17:58:15 -0800340void 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);
Alistair Veitch58a61852016-02-11 13:31:05 -0800347 // Now try writing a valid record.
Alistair Veitch85afe712016-02-02 17:58:15 -0800348 record = census_log_start_write(VALID_SIZE);
349 GPR_ASSERT(record != NULL);
350 census_log_end_write(record, VALID_SIZE);
Alistair Veitch58a61852016-02-11 13:31:05 -0800351 // 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.
Nicolas "Pixel" Noble1faa0362016-03-15 21:28:09 +0100355 // GPR_ASSERT(LOG_SIZE_IN_BYTES - CENSUS_LOG_MAX_RECORD_SIZE ==
356 // census_log_remaining_space());
Alistair Veitch85afe712016-02-02 17:58:15 -0800357 census_log_shutdown();
358}
359
Alistair Veitch58a61852016-02-11 13:31:05 -0800360// Tests end_write() with a different size than what was specified in
361// start_write().
Alistair Veitch85afe712016-02-02 17:58:15 -0800362void 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
Alistair Veitch58a61852016-02-11 13:31:05 -0800379// Verifies that pending records are not available via read_next().
Alistair Veitch85afe712016-02-02 17:58:15 -0800380void 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);
Alistair Veitch58a61852016-02-11 13:31:05 -0800384 // Start a write.
Alistair Veitch85afe712016-02-02 17:58:15 -0800385 void* record_written = census_log_start_write(PR_RECORD_SIZE);
386 GPR_ASSERT(record_written != NULL);
Alistair Veitch58a61852016-02-11 13:31:05 -0800387 // As write is pending, read should fail.
Alistair Veitch85afe712016-02-02 17:58:15 -0800388 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);
Alistair Veitch58a61852016-02-11 13:31:05 -0800392 // A read followed by end_write() should succeed.
Alistair Veitch85afe712016-02-02 17:58:15 -0800393 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
Alistair Veitch58a61852016-02-11 13:31:05 -0800402// Tries reading beyond pending write.
Alistair Veitch85afe712016-02-02 17:58:15 -0800403void test_read_beyond_pending_record(void) {
404 printf("Starting test: read beyond pending record\n");
405 setup_test(0);
Alistair Veitch58a61852016-02-11 13:31:05 -0800406 // Start a write.
Alistair Veitch85afe712016-02-02 17:58:15 -0800407 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);
Alistair Veitch58a61852016-02-11 13:31:05 -0800415 // Now iterate over blocks to read completed records.
Alistair Veitch85afe712016-02-02 17:58:15 -0800416 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);
Alistair Veitch58a61852016-02-11 13:31:05 -0800421 // Complete first record.
Alistair Veitch85afe712016-02-02 17:58:15 -0800422 census_log_end_write(incomplete_record, incomplete_record_size);
Alistair Veitch58a61852016-02-11 13:31:05 -0800423 // 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
Alistair Veitch85afe712016-02-02 17:58:15 -0800426 record_read = census_log_read_next(&bytes_available);
427 GPR_ASSERT(record_read == NULL);
Alistair Veitch58a61852016-02-11 13:31:05 -0800428 // Reset reader to get the newly completed record.
Alistair Veitch85afe712016-02-02 17:58:15 -0800429 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
Alistair Veitch58a61852016-02-11 13:31:05 -0800437// Tests scenario where block being read is detached from a core and put on the
438// dirty list.
Alistair Veitch85afe712016-02-02 17:58:15 -0800439void test_detached_while_reading(void) {
440 printf("Starting test: detached while reading\n");
441 setup_test(0);
Alistair Veitch58a61852016-02-11 13:31:05 -0800442 // Start a write.
Alistair Veitch85afe712016-02-02 17:58:15 -0800443 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);
Alistair Veitch58a61852016-02-11 13:31:05 -0800447 // Read this record.
Alistair Veitch85afe712016-02-02 17:58:15 -0800448 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);
Alistair Veitch58a61852016-02-11 13:31:05 -0800453 // Now fill the log. This will move the block being read from core-local
454 // array to the dirty list.
Alistair Veitch85afe712016-02-02 17:58:15 -0800455 while ((record_written = census_log_start_write(DWR_RECORD_SIZE))) {
456 census_log_end_write(record_written, DWR_RECORD_SIZE);
457 }
458
Alistair Veitch58a61852016-02-11 13:31:05 -0800459 // 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.
Alistair Veitch85afe712016-02-02 17:58:15 -0800463 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
Alistair Veitch58a61852016-02-11 13:31:05 -0800471// Fills non-circular log with records sized such that size is a multiple of
472// CENSUS_LOG_MAX_RECORD_SIZE (no per-block fragmentation).
Alistair Veitch85afe712016-02-02 17:58:15 -0800473void 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
Alistair Veitch58a61852016-02-11 13:31:05 -0800481// Fills circular log with records sized such that size is a multiple of
482// CENSUS_LOG_MAX_RECORD_SIZE (no per-block fragmentation).
Alistair Veitch85afe712016-02-02 17:58:15 -0800483void 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
Alistair Veitch58a61852016-02-11 13:31:05 -0800491// Fills non-circular log with records that may straddle end of a block.
Alistair Veitch85afe712016-02-02 17:58:15 -0800492void 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
Alistair Veitch58a61852016-02-11 13:31:05 -0800500// Fills circular log with records that may straddle end of a block.
Alistair Veitch85afe712016-02-02 17:58:15 -0800501void 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
Alistair Veitch58a61852016-02-11 13:31:05 -0800509// Tests scenario where multiple writers and a single reader are using a log
510// that is configured to discard old records.
Alistair Veitch85afe712016-02-02 17:58:15 -0800511void 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
Alistair Veitch58a61852016-02-11 13:31:05 -0800519// Tests scenario where multiple writers and a single reader are using a log
520// that is configured to discard old records.
Alistair Veitch85afe712016-02-02 17:58:15 -0800521void 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
Alistair Veitch58a61852016-02-11 13:31:05 -0800529// Repeat the straddling records and multiple writers tests with a small log.
Alistair Veitch85afe712016-02-02 17:58:15 -0800530void 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
543void 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
571int main(int argc, char** argv) {
572 grpc_test_init(argc, argv);
Alistair Veitch49653c52016-02-05 11:59:29 -0800573 gpr_time_init();
Alistair Veitch85afe712016-02-02 17:58:15 -0800574 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();
Alistair Veitch58a61852016-02-11 13:31:05 -0800587 test_performance();
Alistair Veitch85afe712016-02-02 17:58:15 -0800588 return 0;
589}