Browse Source

More unit tests for OOM handling.

This gets us up to no interesting untested new or changed lines for
the 10169 code.
Nick Mathewson 10 years ago
parent
commit
c8d41da52d
3 changed files with 159 additions and 7 deletions
  1. 3 3
      src/or/circuitlist.c
  2. 3 0
      src/or/circuitlist.h
  3. 153 4
      src/test/test_oom.c

+ 3 - 3
src/or/circuitlist.c

@@ -1664,7 +1664,7 @@ n_cells_in_circ_queues(const circuit_t *c)
  * This function will return incorrect results if the oldest cell queued on
  * the circuit is older than 2**32 msec (about 49 days) old.
  */
-static uint32_t
+STATIC uint32_t
 circuit_max_queued_cell_age(const circuit_t *c, uint32_t now)
 {
   uint32_t age = 0;
@@ -1711,7 +1711,7 @@ circuit_get_streams_max_data_age(const edge_connection_t *stream, uint32_t now)
 /** Return the age in milliseconds of the oldest buffer chunk on any stream
  * attached to the circuit <b>c</b>, where age is taken in milliseconds before
  * the time <b>now</b> (in truncated milliseconds since the epoch). */
-static uint32_t
+STATIC uint32_t
 circuit_max_queued_data_age(const circuit_t *c, uint32_t now)
 {
   if (CIRCUIT_IS_ORIGIN(c)) {
@@ -1726,7 +1726,7 @@ circuit_max_queued_data_age(const circuit_t *c, uint32_t now)
 /** Return the age of the oldest cell or stream buffer chunk on the circuit
  * <b>c</b>, where age is taken in milliseconds before the time <b>now</b> (in
  * truncated milliseconds since the epoch). */
-static uint32_t
+STATIC uint32_t
 circuit_max_queued_item_age(const circuit_t *c, uint32_t now)
 {
   uint32_t cell_age = circuit_max_queued_cell_age(c, now);

+ 3 - 0
src/or/circuitlist.h

@@ -76,6 +76,9 @@ void channel_note_destroy_not_pending(channel_t *chan, circid_t id);
 #ifdef CIRCUITLIST_PRIVATE
 STATIC void circuit_free(circuit_t *circ);
 STATIC size_t n_cells_in_circ_queues(const circuit_t *c);
+STATIC uint32_t circuit_max_queued_data_age(const circuit_t *c, uint32_t now);
+STATIC uint32_t circuit_max_queued_cell_age(const circuit_t *c, uint32_t now);
+STATIC uint32_t circuit_max_queued_item_age(const circuit_t *c, uint32_t now);
 #endif
 
 #endif

+ 153 - 4
src/test/test_oom.c

@@ -76,7 +76,6 @@ dummy_origin_circuit_new(int n_cells)
   return TO_CIRCUIT(circ);
 }
 
-#if 0
 static void
 add_bytes_to_buf(generic_buffer_t *buf, size_t n_bytes)
 {
@@ -91,18 +90,34 @@ add_bytes_to_buf(generic_buffer_t *buf, size_t n_bytes)
 }
 
 static edge_connection_t *
-dummy_edge_conn_new(int type, size_t in_bytes, size_t out_bytes)
+dummy_edge_conn_new(circuit_t *circ,
+                    int type, size_t in_bytes, size_t out_bytes)
 {
-  edge_connection_t *conn = edge_connection_new(type, AF_INET);
+  edge_connection_t *conn;
+
+  if (type == CONN_TYPE_EXIT)
+    conn = edge_connection_new(type, AF_INET);
+  else
+    conn = ENTRY_TO_EDGE_CONN(entry_connection_new(type, AF_INET));
 
   /* We add these bytes directly to the buffers, to avoid all the
    * edge connection read/write machinery. */
   add_bytes_to_buf(TO_CONN(conn)->inbuf, in_bytes);
   add_bytes_to_buf(TO_CONN(conn)->outbuf, out_bytes);
 
+  conn->on_circuit = circ;
+  if (type == CONN_TYPE_EXIT) {
+    or_circuit_t *oc  = TO_OR_CIRCUIT(circ);
+    conn->next_stream = oc->n_streams;
+    oc->n_streams = conn;
+  } else {
+    origin_circuit_t *oc = TO_ORIGIN_CIRCUIT(circ);
+    conn->next_stream = oc->p_streams;
+    oc->p_streams = conn;
+  }
+
   return conn;
 }
-#endif
 
 /** Run unit tests for buffers.c */
 static void
@@ -192,8 +207,142 @@ test_oom_circbuf(void *arg)
   UNMOCK(circuit_mark_for_close_);
 }
 
+/** Run unit tests for buffers.c */
+static void
+test_oom_streambuf(void *arg)
+{
+  or_options_t *options = get_options_mutable();
+  circuit_t *c1 = NULL, *c2 = NULL, *c3 = NULL, *c4 = NULL, *c5 = NULL;
+  struct timeval tv = { 1389641159, 0 };
+  uint32_t tvms;
+  int i;
+
+  (void) arg;
+
+  MOCK(circuit_mark_for_close_, circuit_mark_for_close_dummy_);
+  init_cell_pool();
+
+  /* Far too low for real life. */
+  options->MaxMemInQueues = 81*packed_cell_mem_cost() + 4096 * 34;
+  options->CellStatistics = 0;
+
+  tt_int_op(cell_queues_check_size(), ==, 0); /* We don't start out OOM. */
+  tt_int_op(cell_queues_get_total_allocation(), ==, 0);
+  tt_int_op(buf_get_total_allocation(), ==, 0);
+
+  /* Start all circuits with a bit of data queued in cells */
+  tv.tv_usec = 500*1000; /* go halfway into the second. */
+  tor_gettimeofday_cache_set(&tv);
+  c1 = dummy_or_circuit_new(10,10);
+  tv.tv_usec = 510*1000;
+  tor_gettimeofday_cache_set(&tv);
+  c2 = dummy_origin_circuit_new(20);
+  tv.tv_usec = 520*1000;
+  tor_gettimeofday_cache_set(&tv);
+  c3 = dummy_or_circuit_new(20,20);
+  tv.tv_usec = 530*1000;
+  tor_gettimeofday_cache_set(&tv);
+  c4 = dummy_or_circuit_new(0,0);
+  tt_int_op(cell_queues_get_total_allocation(), ==,
+            packed_cell_mem_cost() * 80);
+
+  tv.tv_usec = 600*1000;
+  tor_gettimeofday_cache_set(&tv);
+
+  /* Add some connections to c1...c4. */
+  for (i = 0; i < 4; ++i) {
+    edge_connection_t *ec;
+    /* link it to a circuit */
+    tv.tv_usec += 10*1000;
+    tor_gettimeofday_cache_set(&tv);
+    ec = dummy_edge_conn_new(c1, CONN_TYPE_EXIT, 1000, 1000);
+    tt_assert(ec);
+    tv.tv_usec += 10*1000;
+    tor_gettimeofday_cache_set(&tv);
+    ec = dummy_edge_conn_new(c2, CONN_TYPE_AP, 1000, 1000);
+    tt_assert(ec);
+    tv.tv_usec += 10*1000;
+    tor_gettimeofday_cache_set(&tv);
+    ec = dummy_edge_conn_new(c4, CONN_TYPE_EXIT, 1000, 1000); /* Yes, 4 twice*/
+    tt_assert(ec);
+    tv.tv_usec += 10*1000;
+    tor_gettimeofday_cache_set(&tv);
+    ec = dummy_edge_conn_new(c4, CONN_TYPE_EXIT, 1000, 1000);
+    tt_assert(ec);
+  }
+
+  tv.tv_sec += 1;
+  tv.tv_usec = 0;
+  tvms = (uint32_t) tv_to_msec(&tv);
+
+  tt_int_op(circuit_max_queued_cell_age(c1, tvms), ==, 500);
+  tt_int_op(circuit_max_queued_cell_age(c2, tvms), ==, 490);
+  tt_int_op(circuit_max_queued_cell_age(c3, tvms), ==, 480);
+  tt_int_op(circuit_max_queued_cell_age(c4, tvms), ==, 0);
+
+  tt_int_op(circuit_max_queued_data_age(c1, tvms), ==, 390);
+  tt_int_op(circuit_max_queued_data_age(c2, tvms), ==, 380);
+  tt_int_op(circuit_max_queued_data_age(c3, tvms), ==, 0);
+  tt_int_op(circuit_max_queued_data_age(c4, tvms), ==, 370);
+
+  tt_int_op(circuit_max_queued_item_age(c1, tvms), ==, 500);
+  tt_int_op(circuit_max_queued_item_age(c2, tvms), ==, 490);
+  tt_int_op(circuit_max_queued_item_age(c3, tvms), ==, 480);
+  tt_int_op(circuit_max_queued_item_age(c4, tvms), ==, 370);
+
+  tt_int_op(cell_queues_get_total_allocation(), ==,
+            packed_cell_mem_cost() * 80);
+  tt_int_op(buf_get_total_allocation(), ==, 4096*16*2);
+
+  /* Now give c4 a very old buffer of modest size */
+  {
+    edge_connection_t *ec;
+    tv.tv_sec -= 1;
+    tv.tv_usec = 0;
+    tor_gettimeofday_cache_set(&tv);
+    ec = dummy_edge_conn_new(c4, CONN_TYPE_EXIT, 1000, 1000);
+    tt_assert(ec);
+  }
+  tt_int_op(buf_get_total_allocation(), ==, 4096*17*2);
+  tt_int_op(circuit_max_queued_item_age(c4, tvms), ==, 1000);
+
+  tt_int_op(cell_queues_check_size(), ==, 0);
+
+  /* And run over the limit. */
+  tv.tv_usec = 800*1000;
+  tor_gettimeofday_cache_set(&tv);
+  c5 = dummy_or_circuit_new(0,5);
+
+  tt_int_op(cell_queues_get_total_allocation(), ==,
+            packed_cell_mem_cost() * 85);
+  tt_int_op(buf_get_total_allocation(), ==, 4096*17*2);
+
+  tt_int_op(cell_queues_check_size(), ==, 1); /* We are now OOM */
+
+  /* C4 should have died. */
+  tt_assert(! c1->marked_for_close);
+  tt_assert(! c2->marked_for_close);
+  tt_assert(! c3->marked_for_close);
+  tt_assert(c4->marked_for_close);
+  tt_assert(! c5->marked_for_close);
+
+  tt_int_op(cell_queues_get_total_allocation(), ==,
+            packed_cell_mem_cost() * 85);
+  tt_int_op(buf_get_total_allocation(), ==, 4096*8*2);
+
+ done:
+  circuit_free(c1);
+  circuit_free(c2);
+  circuit_free(c3);
+  circuit_free(c4);
+  circuit_free(c5);
+
+  UNMOCK(circuit_mark_for_close_);
+}
+
 struct testcase_t oom_tests[] = {
   { "circbuf", test_oom_circbuf, TT_FORK, NULL, NULL },
+  { "streambuf", test_oom_streambuf, TT_FORK, NULL, NULL },
   END_OF_TESTCASES
 };