tracking down insane socket tester bugs on windoze, where the main culprit so far...
[feisty_meow.git] / octopi / library / tests_sockets / spocket_tester.cpp
1 /*****************************************************************************\
2 *                                                                             *
3 *  Name   : spocket_tester                                                    *
4 *  Author : Chris Koeritz                                                     *
5 *                                                                             *
6 *******************************************************************************
7 * Copyright (c) 2000-$now By Author.  This program is free software; you can  *
8 * redistribute it and/or modify it under the terms of the GNU General Public  *
9 * License as published by the Free Software Foundation; either version 2 of   *
10 * the License or (at your option) any later version.  This is online at:      *
11 *     http://www.fsf.org/copyleft/gpl.html                                    *
12 * Please send any updates to: fred@gruntose.com                               *
13 \*****************************************************************************/
14
15 #include "spocket_tester.h"
16
17 #include <basis/byte_array.h>
18 #include <basis/functions.h>
19 #include <basis/astring.h>
20 #include <loggers/critical_events.h>
21 #include <loggers/program_wide_logger.h>
22 #include <mathematics/chaos.h>
23 #include <sockets/internet_address.h>
24 #include <sockets/raw_socket.h>
25 #include <sockets/spocket.h>
26 #include <sockets/tcpip_definitions.h>
27 #include <sockets/tcpip_stack.h>
28 #include <timely/time_control.h>
29 #include <timely/time_stamp.h>
30
31 #include <errno.h>
32
33 using namespace basis;
34 using namespace loggers;
35 using namespace mathematics;
36 using namespace sockets;
37 using namespace structures;
38 using namespace textual;
39 using namespace timely;
40
41 #define LOG(to_print) EMERGENCY_LOG(program_wide_logger().get(), astring(to_print))
42
43 const int MAXIMUM_WINSOCK_MTU = 100000;
44   // the largest chunk of bytes we will receive at one time.
45
46 const int MAXIMUM_TRANSFER_WAIT = 40 * SECOND_ms;
47   // the longest amount of time we wait in trying to receive data.
48
49 static abyte receive_buffer[MAXIMUM_WINSOCK_MTU + 1];
50   // used for dumping received data into.
51
52 const int PAUSE_TIME = 200;
53   // the snooze interval when we encounter socket underflow or overflow.
54
55 #define DEBUG_SPOCKET_TESTER
56   // uncomment for noisy version.
57
58 spocket_tester::spocket_tester(const internet_address &where)
59 : _where(new internet_address(where)),
60   _stack(new tcpip_stack),
61   _socket(NIL),
62   _root_server(NIL),
63   _raw(new raw_socket)
64 {
65 }
66
67 spocket_tester::~spocket_tester()
68 {
69   WHACK(_socket);
70   WHACK(_root_server);
71   WHACK(_stack);
72   WHACK(_where);
73   WHACK(_raw);
74 }
75
76 bool spocket_tester::connect()
77 {
78   if (!_socket) {
79 LOG("creating new client socket");
80     _socket = new spocket(*_where);
81   }
82 LOG(astring("connect socket info: ") + _socket->text_form());
83   outcome ret = spocket::NO_CONNECTION;
84   while (true) {
85     ret = _socket->connect();
86     if (ret == spocket::OKAY) break;
87     if (ret != spocket::NO_CONNECTION) break;
88     time_control::sleep_ms(100);
89   }
90   return ret == spocket::OKAY;
91 }
92
93 bool spocket_tester::accept(bool wait)
94 {
95   if (!_root_server) {
96 LOG("hey creating new root server socket");
97     _root_server = new spocket(*_where);
98 LOG("got past creating the root server socket");
99 LOG(astring("accept root server socket info: ") + _root_server->text_form());
100   }
101   if (_socket) {
102     LOG("already have a socket for accept!");
103     return true;
104   }
105   outcome ret = spocket::NO_CONNECTION;
106 LOG("int accepting loop on root server");
107   while (true) {
108     ret = _root_server->accept(_socket, false);
109     if (ret == spocket::OKAY) break;
110     if (ret != spocket::NO_CONNECTION) break;
111     if (!wait) return true;  // we tried to accept at least once.
112     time_control::sleep_ms(100);  // snooze to avoid slamming with accepts.
113   }
114
115 if (_socket != NIL) {
116 LOG(astring("accepted! socket info: ") + _socket->text_form());
117 }
118   return ret == spocket::OKAY;
119 }
120
121 bool spocket_tester::do_a_send(abyte *buffer, int size,
122     testing_statistics &stats)
123 {
124   time_stamp start_time;
125   int len_sent;
126   time_stamp when_to_leave(MAXIMUM_TRANSFER_WAIT);
127   outcome worked;
128
129 #ifdef DEBUG_SPOCKET_TESTER
130   LOG("into do a send");
131 #endif
132
133   while (time_stamp() < when_to_leave) {
134     worked = _socket->send(buffer, size, len_sent);
135     if (worked == spocket::NONE_READY) {
136 ////      time_control::sleep_ms(PAUSE_TIME);
137       _socket->await_writable(PAUSE_TIME);
138       continue;
139     } else if (worked == spocket::PARTIAL) {
140 //danger danger if we get wrong info.
141       buffer += len_sent;
142       size -= len_sent;
143       stats.bytes_sent += len_sent;
144 ///      time_control::sleep_ms(PAUSE_TIME);
145       _socket->await_writable(PAUSE_TIME);
146       continue;
147     } else break;
148   }
149 #ifdef DEBUG_SPOCKET_TESTER
150   LOG("got out of loop");
151 #endif
152
153   stats.send_time += int(time_stamp().value() - start_time.value());
154   stats.bytes_sent += len_sent;
155
156   if ( (worked != spocket::OKAY) && (worked != spocket::PARTIAL) ) {
157     LOG(astring("No data went out on the socket: ")
158         + spocket::outcome_name(worked));
159     return false;
160   }
161   if (len_sent != size) {
162     LOG(a_sprintf("partial send on socket, %d bytes instead of %d, recurse.", 
163         len_sent, size));
164     return do_a_send(buffer + len_sent, size - len_sent, stats);
165   }
166
167   time_stamp end_time;
168 //  int time_taken = int(end_time.value() - start_time.value());
169
170   return true;
171 }
172
173 bool spocket_tester::do_a_receive(int size_expected, testing_statistics &stats)
174 {
175   time_stamp start_time;
176
177 #ifdef DEBUG_SPOCKET_TESTER
178   LOG("into do a rcv");
179 #endif
180
181   time_stamp when_to_leave(MAXIMUM_TRANSFER_WAIT);
182 LOG("FARP 1");
183   int full_length = 0;
184   while ( (full_length < size_expected) && (time_stamp() < when_to_leave) ) {
185 LOG("FARP 2");
186     time_stamp start_of_receive;
187     int len = MAXIMUM_WINSOCK_MTU;
188 LOG("FARP 3");
189 if (!_socket) {
190   LOG("WHAT?  SOCKET IS NULL!!!");
191 }
192 ////time_control::sleep_ms(PAUSE_TIME);
193 LOG("FARP 3.1");
194 LOG(astring("socket info: ") + _socket->text_form());
195 LOG("FARP 3.2");
196     outcome ret = _socket->receive(receive_buffer, len);
197 LOG("FARP 4");
198     if (ret != spocket::OKAY) {
199       if (ret == spocket::NONE_READY) {
200 if (len != 0) LOG(a_sprintf("supposedly nothing was received (%d bytes)", len));
201 ///        time_control::sleep_ms(PAUSE_TIME);
202         _socket->await_readable(PAUSE_TIME);
203         continue;
204       } else break;
205     }
206     // reset our time if we've gotten good data.
207     if (ret == spocket::OKAY)
208       when_to_leave.reset(MAXIMUM_TRANSFER_WAIT);
209
210     int receive_duration = int(time_stamp().value()
211         - start_of_receive.value());
212     stats.receive_time += receive_duration;
213
214 #ifdef DEBUG_SPOCKET_TESTER
215     LOG(a_sprintf("did recv, len=%d", len));
216 #endif
217
218     if (!len) {
219       LOG("Our socket has been disconnected.");
220       return false;
221     } else if (len < 0) {
222       if (errno == SOCK_EWOULDBLOCK) continue;  // no data.
223       LOG(astring("The receive failed with an error ")
224           + critical_events::system_error_text(errno));
225       return false;
226     }
227     full_length += len;
228     stats.bytes_received += len;
229   }
230
231   if (full_length != size_expected)
232     LOG(a_sprintf("Did not get the full size expected (wanted %d and "
233         "got %d bytes).", size_expected, full_length));
234
235   return true;
236 }
237
238 bool spocket_tester::perform_test(int size, int count,
239     testing_statistics &stats)
240 {
241 #ifdef DEBUG_SPOCKET_TESTER
242   LOG("into perf test");
243 #endif
244
245   // the statics are used to generate our random buffer for sending.
246   static abyte garbage_buffer[MAXIMUM_WINSOCK_MTU + 1];
247   static bool garbage_initialized = false;
248   chaos randomizer;
249
250   // if our static buffer full of random stuff was never initialized, we do
251   // so now.  this supports efficiently re-using the tester if desired.
252   if (!garbage_initialized) {
253     LOG("initializing random send buffer.");
254     // note the less than or equal; we know we have one more byte to fill.
255     for (int i = 0; i <= MAXIMUM_WINSOCK_MTU; i++)
256       garbage_buffer[i] = randomizer.inclusive(0, 255);
257     garbage_initialized = true;
258     LOG("random send buffer initialized.");
259   }
260
261   // reset the statistical package.
262   stats.total_runs = 0;
263   stats.send_time = 0;
264   stats.receive_time = 0;
265   stats.bytes_sent = 0;
266   stats.bytes_received = 0;
267
268   // check that they aren't trying to do too big of a send.
269   if (size > MAXIMUM_WINSOCK_MTU) {
270     LOG("The size is over our limit.  To fix this, edit the "
271         "send_data function.");
272     return false;
273   }
274
275   // check that our socket is usable.
276   if (!_socket) {
277     LOG("One cannot send data on an uninitialized tester!");
278     return false;
279   }
280
281   int runs_completed = 0;
282     // counts up how many times we've done our test cycle.
283
284   while (runs_completed < count) {
285 #ifdef DEBUG_SPOCKET_TESTER
286     LOG(a_sprintf("iter %d", runs_completed));
287 #endif
288     if (_socket->client()) {
289       // we're doing the client side routine here.
290       time_stamp trip_start;
291 #ifdef DEBUG_SPOCKET_TESTER
292       LOG("client about to send");
293 #endif
294       if (!do_a_send(garbage_buffer, size, stats)) {
295         LOG("We failed on a send.  Now quitting.");
296         return false;
297       }
298 #ifdef DEBUG_SPOCKET_TESTER
299       LOG("client about to rcv");
300 #endif
301       if (!do_a_receive(size, stats)) {
302         LOG("We failed on a receive.  Now quitting.");
303         return false;
304       }
305       stats.round_trip_time += int(time_stamp().value() - trip_start.value());
306     } else {
307       // we're doing the server side routine here.
308       time_stamp trip_start;
309 #ifdef DEBUG_SPOCKET_TESTER
310       LOG("server about to rcv");
311 #endif
312       if (!do_a_receive(size, stats)) {
313         LOG("We failed on a receive.  Now quitting.");
314         return false;
315       }
316 #ifdef DEBUG_SPOCKET_TESTER
317       LOG("server about to send");
318 #endif
319       if (!do_a_send(garbage_buffer, size, stats)) {
320         LOG("We failed on a send.  Now quitting.");
321         return false;
322       }
323       stats.round_trip_time += int(time_stamp().value() - trip_start.value());
324     }
325
326     runs_completed++;  // finished a run.
327     stats.total_runs++;  // count it in the overall stats too.
328     if ( !(runs_completed % 10) )
329       LOG(a_sprintf("Completed test #%d.", runs_completed));
330   }
331
332   return true;
333 }
334