|
@@ -193,7 +193,120 @@ test_buffers_basic(void *arg)
|
|
buf_free(buf);
|
|
buf_free(buf);
|
|
if (buf2)
|
|
if (buf2)
|
|
buf_free(buf2);
|
|
buf_free(buf2);
|
|
|
|
+ buf_shrink_freelists(1);
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+test_buffer_pullup(void *arg)
|
|
|
|
+{
|
|
|
|
+ buf_t *buf;
|
|
|
|
+ char *stuff, *tmp;
|
|
|
|
+ const char *cp;
|
|
|
|
+ size_t sz;
|
|
|
|
+ (void)arg;
|
|
|
|
+ stuff = tor_malloc(16384);
|
|
|
|
+ tmp = tor_malloc(16384);
|
|
|
|
+
|
|
|
|
+ /* Note: this test doesn't check the nulterminate argument to buf_pullup,
|
|
|
|
+ since nothing actually uses it. We should remove it some time. */
|
|
|
|
+
|
|
|
|
+ buf = buf_new_with_capacity(3000); /* rounds up to next power of 2. */
|
|
|
|
+
|
|
|
|
+ tt_assert(buf);
|
|
|
|
+ tt_int_op(buf_get_default_chunk_size(buf), ==, 4096);
|
|
|
|
+
|
|
|
|
+ tt_int_op(buf_get_total_allocation(), ==, 0);
|
|
|
|
+
|
|
|
|
+ /* There are a bunch of cases for pullup. One is the trivial case. Let's
|
|
|
|
+ mess around with an empty buffer. */
|
|
|
|
+ buf_pullup(buf, 16, 1);
|
|
|
|
+ buf_get_first_chunk_data(buf, &cp, &sz);
|
|
|
|
+ tt_ptr_op(cp, ==, NULL);
|
|
|
|
+ tt_ptr_op(sz, ==, 0);
|
|
|
|
+
|
|
|
|
+ /* Let's make sure nothing got allocated */
|
|
|
|
+ tt_int_op(buf_get_total_allocation(), ==, 0);
|
|
|
|
+
|
|
|
|
+ /* Case 1: everything puts into the first chunk with some moving. */
|
|
|
|
+
|
|
|
|
+ /* Let's add some data. */
|
|
|
|
+ crypto_rand(stuff, 16384);
|
|
|
|
+ write_to_buf(stuff, 3000, buf);
|
|
|
|
+ write_to_buf(stuff+3000, 3000, buf);
|
|
|
|
+ buf_get_first_chunk_data(buf, &cp, &sz);
|
|
|
|
+ tt_ptr_op(cp, !=, NULL);
|
|
|
|
+ tt_int_op(sz, <=, 4096);
|
|
|
|
+
|
|
|
|
+ /* Make room for 3000 bytes in the first chunk, so that the pullup-move code
|
|
|
|
+ * can get tested. */
|
|
|
|
+ tt_int_op(fetch_from_buf(tmp, 3000, buf), ==, 3000);
|
|
|
|
+ test_memeq(tmp, stuff, 3000);
|
|
|
|
+ buf_pullup(buf, 2048, 0);
|
|
|
|
+ assert_buf_ok(buf);
|
|
|
|
+ buf_get_first_chunk_data(buf, &cp, &sz);
|
|
|
|
+ tt_ptr_op(cp, !=, NULL);
|
|
|
|
+ tt_int_op(sz, >=, 2048);
|
|
|
|
+ test_memeq(cp, stuff+3000, 2048);
|
|
|
|
+ tt_int_op(3000, ==, buf_datalen(buf));
|
|
|
|
+ tt_int_op(fetch_from_buf(tmp, 3000, buf), ==, 0);
|
|
|
|
+ test_memeq(tmp, stuff+3000, 2048);
|
|
|
|
+
|
|
|
|
+ buf_free(buf);
|
|
|
|
+
|
|
|
|
+ /* Now try the large-chunk case. */
|
|
|
|
+ buf = buf_new_with_capacity(3000); /* rounds up to next power of 2. */
|
|
|
|
+ write_to_buf(stuff, 4000, buf);
|
|
|
|
+ write_to_buf(stuff+4000, 4000, buf);
|
|
|
|
+ write_to_buf(stuff+8000, 4000, buf);
|
|
|
|
+ write_to_buf(stuff+12000, 4000, buf);
|
|
|
|
+ tt_int_op(buf_datalen(buf), ==, 16000);
|
|
|
|
+ buf_get_first_chunk_data(buf, &cp, &sz);
|
|
|
|
+ tt_ptr_op(cp, !=, NULL);
|
|
|
|
+ tt_int_op(sz, <=, 4096);
|
|
|
|
+
|
|
|
|
+ buf_pullup(buf, 12500, 0);
|
|
|
|
+ assert_buf_ok(buf);
|
|
|
|
+ buf_get_first_chunk_data(buf, &cp, &sz);
|
|
|
|
+ tt_ptr_op(cp, !=, NULL);
|
|
|
|
+ tt_int_op(sz, >=, 12500);
|
|
|
|
+ test_memeq(cp, stuff, 12500);
|
|
|
|
+ tt_int_op(buf_datalen(buf), ==, 16000);
|
|
|
|
+
|
|
|
|
+ fetch_from_buf(tmp, 12400, buf);
|
|
|
|
+ test_memeq(tmp, stuff, 12400);
|
|
|
|
+ tt_int_op(buf_datalen(buf), ==, 3600);
|
|
|
|
+ fetch_from_buf(tmp, 3500, buf);
|
|
|
|
+ test_memeq(tmp, stuff+12400, 3500);
|
|
|
|
+ fetch_from_buf(tmp, 100, buf);
|
|
|
|
+ test_memeq(tmp, stuff+15900, 10);
|
|
|
|
+
|
|
|
|
+ buf_free(buf);
|
|
|
|
+
|
|
|
|
+ /* Make sure that the pull-up-whole-buffer case works */
|
|
|
|
+ buf = buf_new_with_capacity(3000); /* rounds up to next power of 2. */
|
|
|
|
+ write_to_buf(stuff, 4000, buf);
|
|
|
|
+ write_to_buf(stuff+4000, 4000, buf);
|
|
|
|
+ fetch_from_buf(tmp, 100, buf); /* dump 100 bytes from first chunk */
|
|
|
|
+ buf_pullup(buf, 16000, 0); /* Way too much. */
|
|
|
|
+ assert_buf_ok(buf);
|
|
|
|
+ buf_get_first_chunk_data(buf, &cp, &sz);
|
|
|
|
+ tt_ptr_op(cp, !=, NULL);
|
|
|
|
+ tt_int_op(sz, ==, 7900);
|
|
|
|
+ test_memeq(cp, stuff+100, 7900);
|
|
|
|
+
|
|
|
|
+ buf_free(buf);
|
|
|
|
+ buf = NULL;
|
|
|
|
+
|
|
|
|
+ buf_shrink_freelists(1);
|
|
|
|
+
|
|
|
|
+ tt_int_op(buf_get_total_allocation(), ==, 0);
|
|
|
|
+ done:
|
|
|
|
+ buf_free(buf);
|
|
|
|
+ buf_shrink_freelists(1);
|
|
|
|
+ tor_free(stuff);
|
|
|
|
+ tor_free(tmp);
|
|
|
|
+}
|
|
|
|
+
|
|
static void
|
|
static void
|
|
test_buffer_copy(void *arg)
|
|
test_buffer_copy(void *arg)
|
|
{
|
|
{
|
|
@@ -257,6 +370,7 @@ test_buffer_copy(void *arg)
|
|
generic_buffer_free(buf);
|
|
generic_buffer_free(buf);
|
|
if (buf2)
|
|
if (buf2)
|
|
generic_buffer_free(buf2);
|
|
generic_buffer_free(buf2);
|
|
|
|
+ buf_shrink_freelists(1);
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
static void
|
|
@@ -331,12 +445,157 @@ test_buffer_ext_or_cmd(void *arg)
|
|
ext_or_cmd_free(cmd);
|
|
ext_or_cmd_free(cmd);
|
|
generic_buffer_free(buf);
|
|
generic_buffer_free(buf);
|
|
tor_free(tmp);
|
|
tor_free(tmp);
|
|
|
|
+ buf_shrink_freelists(1);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+test_buffer_allocation_tracking(void *arg)
|
|
|
|
+{
|
|
|
|
+ char *junk = tor_malloc(16384);
|
|
|
|
+ buf_t *buf1 = NULL, *buf2 = NULL;
|
|
|
|
+ int i;
|
|
|
|
+
|
|
|
|
+ (void)arg;
|
|
|
|
+
|
|
|
|
+ crypto_rand(junk, 16384);
|
|
|
|
+ tt_int_op(buf_get_total_allocation(), ==, 0);
|
|
|
|
+
|
|
|
|
+ buf1 = buf_new();
|
|
|
|
+ tt_assert(buf1);
|
|
|
|
+ buf2 = buf_new();
|
|
|
|
+ tt_assert(buf2);
|
|
|
|
+
|
|
|
|
+ tt_int_op(buf_allocation(buf1), ==, 0);
|
|
|
|
+ tt_int_op(buf_get_total_allocation(), ==, 0);
|
|
|
|
+
|
|
|
|
+ write_to_buf(junk, 4000, buf1);
|
|
|
|
+ write_to_buf(junk, 4000, buf1);
|
|
|
|
+ write_to_buf(junk, 4000, buf1);
|
|
|
|
+ write_to_buf(junk, 4000, buf1);
|
|
|
|
+ tt_int_op(buf_allocation(buf1), ==, 16384);
|
|
|
|
+ fetch_from_buf(junk, 100, buf1);
|
|
|
|
+ tt_int_op(buf_allocation(buf1), ==, 16384); /* still 4 4k chunks */
|
|
|
|
+
|
|
|
|
+ tt_int_op(buf_get_total_allocation(), ==, 16384);
|
|
|
|
+
|
|
|
|
+ fetch_from_buf(junk, 4096, buf1); /* drop a 1k chunk... */
|
|
|
|
+ tt_int_op(buf_allocation(buf1), ==, 3*4096); /* now 3 4k chunks */
|
|
|
|
+
|
|
|
|
+ tt_int_op(buf_get_total_allocation(), ==, 16384); /* that chunk went onto
|
|
|
|
+ the freelist. */
|
|
|
|
+
|
|
|
|
+ write_to_buf(junk, 4000, buf2);
|
|
|
|
+ tt_int_op(buf_allocation(buf2), ==, 4096); /* another 4k chunk. */
|
|
|
|
+ tt_int_op(buf_get_total_allocation(), ==, 16384); /* that chunk came from
|
|
|
|
+ the freelist. */
|
|
|
|
+ write_to_buf(junk, 4000, buf2);
|
|
|
|
+ tt_int_op(buf_allocation(buf2), ==, 8192); /* another 4k chunk. */
|
|
|
|
+ tt_int_op(buf_get_total_allocation(), ==, 5*4096); /* that chunk was new. */
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /* Make a really huge buffer */
|
|
|
|
+ for (i = 0; i < 1000; ++i) {
|
|
|
|
+ write_to_buf(junk, 4000, buf2);
|
|
|
|
+ }
|
|
|
|
+ tt_int_op(buf_allocation(buf2), >=, 4008000);
|
|
|
|
+ tt_int_op(buf_get_total_allocation(), >=, 4008000);
|
|
|
|
+ buf_free(buf2);
|
|
|
|
+ buf2 = NULL;
|
|
|
|
+
|
|
|
|
+ tt_int_op(buf_get_total_allocation(), <, 4008000);
|
|
|
|
+ buf_shrink_freelists(1);
|
|
|
|
+ tt_int_op(buf_get_total_allocation(), ==, buf_allocation(buf1));
|
|
|
|
+ buf_free(buf1);
|
|
|
|
+ buf1 = NULL;
|
|
|
|
+ buf_shrink_freelists(1);
|
|
|
|
+ tt_int_op(buf_get_total_allocation(), ==, 0);
|
|
|
|
+
|
|
|
|
+ done:
|
|
|
|
+ buf_free(buf1);
|
|
|
|
+ buf_free(buf2);
|
|
|
|
+ buf_shrink_freelists(1);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void
|
|
|
|
+test_buffer_time_tracking(void *arg)
|
|
|
|
+{
|
|
|
|
+ buf_t *buf=NULL, *buf2=NULL;
|
|
|
|
+ struct timeval tv0;
|
|
|
|
+ const time_t START = 1389288246;
|
|
|
|
+ const uint32_t START_MSEC = (uint32_t) ((uint64_t)START * 1000);
|
|
|
|
+ int i;
|
|
|
|
+ char tmp[4096];
|
|
|
|
+ (void)arg;
|
|
|
|
+
|
|
|
|
+ crypto_rand(tmp, sizeof(tmp));
|
|
|
|
+
|
|
|
|
+ tv0.tv_sec = START;
|
|
|
|
+ tv0.tv_usec = 0;
|
|
|
|
+
|
|
|
|
+ buf = buf_new_with_capacity(3000); /* rounds up to next power of 2. */
|
|
|
|
+ tt_assert(buf);
|
|
|
|
+
|
|
|
|
+ /* Empty buffer means the timestamp is 0. */
|
|
|
|
+ tt_int_op(0, ==, buf_get_oldest_chunk_timestamp(buf, START_MSEC));
|
|
|
|
+ tt_int_op(0, ==, buf_get_oldest_chunk_timestamp(buf, START_MSEC+1000));
|
|
|
|
+
|
|
|
|
+ tor_gettimeofday_cache_set(&tv0);
|
|
|
|
+ write_to_buf("ABCDEFG", 7, buf);
|
|
|
|
+ tt_int_op(1000, ==, buf_get_oldest_chunk_timestamp(buf, START_MSEC+1000));
|
|
|
|
+
|
|
|
|
+ buf2 = buf_copy(buf);
|
|
|
|
+ tt_assert(buf2);
|
|
|
|
+ tt_int_op(1234, ==, buf_get_oldest_chunk_timestamp(buf2, START_MSEC+1234));
|
|
|
|
+
|
|
|
|
+ /* Now add more bytes; enough to overflow the first chunk. */
|
|
|
|
+ tv0.tv_usec += 123 * 1000;
|
|
|
|
+ tor_gettimeofday_cache_set(&tv0);
|
|
|
|
+ for (i = 0; i < 600; ++i)
|
|
|
|
+ write_to_buf("ABCDEFG", 7, buf);
|
|
|
|
+ tt_int_op(4207, ==, buf_datalen(buf));
|
|
|
|
+
|
|
|
|
+ /* The oldest bytes are still in the front. */
|
|
|
|
+ tt_int_op(2000, ==, buf_get_oldest_chunk_timestamp(buf, START_MSEC+2000));
|
|
|
|
+
|
|
|
|
+ /* Once those bytes are dropped, the chunk is still on the first
|
|
|
|
+ * timestamp. */
|
|
|
|
+ fetch_from_buf(tmp, 100, buf);
|
|
|
|
+ tt_int_op(2000, ==, buf_get_oldest_chunk_timestamp(buf, START_MSEC+2000));
|
|
|
|
+
|
|
|
|
+ /* But once we discard the whole first chunk, we get the data in the second
|
|
|
|
+ * chunk. */
|
|
|
|
+ fetch_from_buf(tmp, 4000, buf);
|
|
|
|
+ tt_int_op(107, ==, buf_datalen(buf));
|
|
|
|
+ tt_int_op(2000, ==, buf_get_oldest_chunk_timestamp(buf, START_MSEC+2123));
|
|
|
|
+
|
|
|
|
+ /* This time we'll be grabbing a chunk from the freelist, and making sure
|
|
|
|
+ its time gets updated */
|
|
|
|
+ tv0.tv_sec += 5;
|
|
|
|
+ tv0.tv_usec = 617*1000;
|
|
|
|
+ tor_gettimeofday_cache_set(&tv0);
|
|
|
|
+ for (i = 0; i < 600; ++i)
|
|
|
|
+ write_to_buf("ABCDEFG", 7, buf);
|
|
|
|
+ tt_int_op(4307, ==, buf_datalen(buf));
|
|
|
|
+
|
|
|
|
+ tt_int_op(2000, ==, buf_get_oldest_chunk_timestamp(buf, START_MSEC+2123));
|
|
|
|
+ fetch_from_buf(tmp, 4000, buf);
|
|
|
|
+ fetch_from_buf(tmp, 306, buf);
|
|
|
|
+ tt_int_op(0, ==, buf_get_oldest_chunk_timestamp(buf, START_MSEC+5617));
|
|
|
|
+ tt_int_op(383, ==, buf_get_oldest_chunk_timestamp(buf, START_MSEC+6000));
|
|
|
|
+
|
|
|
|
+ done:
|
|
|
|
+ buf_free(buf);
|
|
|
|
+ buf_free(buf2);
|
|
}
|
|
}
|
|
|
|
|
|
struct testcase_t buffer_tests[] = {
|
|
struct testcase_t buffer_tests[] = {
|
|
- { "basic", test_buffers_basic, 0, NULL, NULL },
|
|
|
|
- { "copy", test_buffer_copy, 0, NULL, NULL },
|
|
|
|
- { "ext_or_cmd", test_buffer_ext_or_cmd, 0, NULL, NULL },
|
|
|
|
|
|
+ { "basic", test_buffers_basic, TT_FORK, NULL, NULL },
|
|
|
|
+ { "copy", test_buffer_copy, TT_FORK, NULL, NULL },
|
|
|
|
+ { "pullup", test_buffer_pullup, TT_FORK, NULL, NULL },
|
|
|
|
+ { "ext_or_cmd", test_buffer_ext_or_cmd, TT_FORK, NULL, NULL },
|
|
|
|
+ { "allocation_tracking", test_buffer_allocation_tracking, TT_FORK,
|
|
|
|
+ NULL, NULL },
|
|
|
|
+ { "time_tracking", test_buffer_time_tracking, TT_FORK, NULL, NULL },
|
|
END_OF_TESTCASES
|
|
END_OF_TESTCASES
|
|
};
|
|
};
|
|
|
|
|