Stupid cut-and-paste bug.
[tor.git] / src / or / rephist.c
blob4540cfeca0ba81de9908293aef22c0b3f00522c7
1 /* Copyright 2004-2005 Roger Dingledine, Nick Mathewson. */
2 /* See LICENSE for licensing information */
3 /* $Id$ */
4 const char rephist_c_id[] =
5 "$Id$";
7 /**
8 * \file rephist.c
9 * \brief Basic history and "reputation" functionality to remember
10 * which servers have worked in the past, how much bandwidth we've
11 * been using, which ports we tend to want, and so on.
12 **/
14 #include "or.h"
16 static void bw_arrays_init(void);
17 static void predicted_ports_init(void);
19 uint64_t rephist_total_alloc=0;
20 uint32_t rephist_total_num=0;
22 /** History of an OR-\>OR link. */
23 typedef struct link_history_t {
24 /** When did we start tracking this list? */
25 time_t since;
26 /** When did we most recently note a change to this link */
27 time_t changed;
28 /** How many times did extending from OR1 to OR2 succeed? */
29 unsigned long n_extend_ok;
30 /** How many times did extending from OR1 to OR2 fail? */
31 unsigned long n_extend_fail;
32 } link_history_t;
34 /** History of an OR. */
35 typedef struct or_history_t {
36 /** When did we start tracking this OR? */
37 time_t since;
38 /** When did we most recently note a change to this OR? */
39 time_t changed;
40 /** How many times did we successfully connect? */
41 unsigned long n_conn_ok;
42 /** How many times did we try to connect and fail?*/
43 unsigned long n_conn_fail;
44 /** How many seconds have we been connected to this OR before
45 * 'up_since'? */
46 unsigned long uptime;
47 /** How many seconds have we been unable to connect to this OR before
48 * 'down_since'? */
49 unsigned long downtime;
50 /** If nonzero, we have been connected since this time. */
51 time_t up_since;
52 /** If nonzero, we have been unable to connect since this time. */
53 time_t down_since;
54 /** Map from hex OR2 identity digest to a link_history_t for the link
55 * from this OR to OR2. */
56 digestmap_t *link_history_map;
57 } or_history_t;
59 /** Map from hex OR identity digest to or_history_t. */
60 static digestmap_t *history_map = NULL;
62 /** Return the or_history_t for the named OR, creating it if necessary.
64 static or_history_t *
65 get_or_history(const char* id)
67 or_history_t *hist;
69 if (!memcmp(id, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", DIGEST_LEN))
70 return NULL;
72 hist = digestmap_get(history_map, id);
73 if (!hist) {
74 hist = tor_malloc_zero(sizeof(or_history_t));
75 rephist_total_alloc += sizeof(or_history_t);
76 rephist_total_num++;
77 hist->link_history_map = digestmap_new();
78 hist->since = hist->changed = time(NULL);
79 digestmap_set(history_map, id, hist);
81 return hist;
84 /** Return the link_history_t for the link from the first named OR to
85 * the second, creating it if necessary. (ORs are identified by
86 * identity digest)
88 static link_history_t *
89 get_link_history(const char *from_id, const char *to_id)
91 or_history_t *orhist;
92 link_history_t *lhist;
93 orhist = get_or_history(from_id);
94 if (!orhist)
95 return NULL;
96 if (!memcmp(to_id, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", DIGEST_LEN))
97 return NULL;
98 lhist = (link_history_t*) digestmap_get(orhist->link_history_map, to_id);
99 if (!lhist) {
100 lhist = tor_malloc_zero(sizeof(link_history_t));
101 rephist_total_alloc += sizeof(link_history_t);
102 lhist->since = lhist->changed = time(NULL);
103 digestmap_set(orhist->link_history_map, to_id, lhist);
105 return lhist;
108 /** Helper: free storage held by a single link history entry */
109 static void
110 _free_link_history(void *val)
112 rephist_total_alloc -= sizeof(link_history_t);
113 tor_free(val);
116 /** Helper: free storage held by a single OR history entry */
117 static void
118 free_or_history(void *_hist)
120 or_history_t *hist = _hist;
121 digestmap_free(hist->link_history_map, _free_link_history);
122 rephist_total_alloc -= sizeof(or_history_t);
123 rephist_total_num--;
124 tor_free(hist);
127 /** Update an or_history_t object <b>hist</b> so that its uptime/downtime
128 * count is up-to-date as of <b>when</b>.
130 static void
131 update_or_history(or_history_t *hist, time_t when)
133 tor_assert(hist);
134 if (hist->up_since) {
135 tor_assert(!hist->down_since);
136 hist->uptime += (when - hist->up_since);
137 hist->up_since = when;
138 } else if (hist->down_since) {
139 hist->downtime += (when - hist->down_since);
140 hist->down_since = when;
144 /** Initialize the static data structures for tracking history.
146 void
147 rep_hist_init(void)
149 history_map = digestmap_new();
150 bw_arrays_init();
151 predicted_ports_init();
154 /** Remember that an attempt to connect to the OR with identity digest
155 * <b>id</b> failed at <b>when</b>.
157 void
158 rep_hist_note_connect_failed(const char* id, time_t when)
160 or_history_t *hist;
161 hist = get_or_history(id);
162 if (!hist)
163 return;
164 ++hist->n_conn_fail;
165 if (hist->up_since) {
166 hist->uptime += (when - hist->up_since);
167 hist->up_since = 0;
169 if (!hist->down_since)
170 hist->down_since = when;
171 hist->changed = when;
174 /** Remember that an attempt to connect to the OR with identity digest
175 * <b>id</b> succeeded at <b>when</b>.
177 void
178 rep_hist_note_connect_succeeded(const char* id, time_t when)
180 or_history_t *hist;
181 hist = get_or_history(id);
182 if (!hist)
183 return;
184 ++hist->n_conn_ok;
185 if (hist->down_since) {
186 hist->downtime += (when - hist->down_since);
187 hist->down_since = 0;
189 if (!hist->up_since)
190 hist->up_since = when;
191 hist->changed = when;
194 /** Remember that we intentionally closed our connection to the OR
195 * with identity digest <b>id</b> at <b>when</b>.
197 void
198 rep_hist_note_disconnect(const char* id, time_t when)
200 or_history_t *hist;
201 hist = get_or_history(id);
202 if (!hist)
203 return;
204 ++hist->n_conn_ok;
205 if (hist->up_since) {
206 hist->uptime += (when - hist->up_since);
207 hist->up_since = 0;
209 hist->changed = when;
212 /** Remember that our connection to the OR with identity digest
213 * <b>id</b> had an error and stopped working at <b>when</b>.
215 void
216 rep_hist_note_connection_died(const char* id, time_t when)
218 or_history_t *hist;
219 if (!id) {
220 /* XXXX009 Well, everybody has an ID now. Hm. */
221 /* If conn has no nickname, it's either an OP, or it is an OR
222 * which didn't complete its handshake (or did and was unapproved).
223 * Ignore it.
225 return;
227 hist = get_or_history(id);
228 if (!hist)
229 return;
230 if (hist->up_since) {
231 hist->uptime += (when - hist->up_since);
232 hist->up_since = 0;
234 if (!hist->down_since)
235 hist->down_since = when;
236 hist->changed = when;
239 /** Remember that we successfully extended from the OR with identity
240 * digest <b>from_id</b> to the OR with identity digest
241 * <b>to_name</b>.
243 void
244 rep_hist_note_extend_succeeded(const char *from_id, const char *to_id)
246 link_history_t *hist;
247 /* log_fn(LOG_WARN, "EXTEND SUCCEEDED: %s->%s",from_name,to_name); */
248 hist = get_link_history(from_id, to_id);
249 if (!hist)
250 return;
251 ++hist->n_extend_ok;
252 hist->changed = time(NULL);
255 /** Remember that we tried to extend from the OR with identity digest
256 * <b>from_id</b> to the OR with identity digest <b>to_name</b>, but
257 * failed.
259 void
260 rep_hist_note_extend_failed(const char *from_id, const char *to_id)
262 link_history_t *hist;
263 /* log_fn(LOG_WARN, "EXTEND FAILED: %s->%s",from_name,to_name); */
264 hist = get_link_history(from_id, to_id);
265 if (!hist)
266 return;
267 ++hist->n_extend_fail;
268 hist->changed = time(NULL);
271 /** Log all the reliability data we have remembered, with the chosen
272 * severity.
274 void
275 rep_hist_dump_stats(time_t now, int severity)
277 digestmap_iter_t *lhist_it;
278 digestmap_iter_t *orhist_it;
279 const char *name1, *name2, *digest1, *digest2;
280 char hexdigest1[HEX_DIGEST_LEN+1];
281 or_history_t *or_history;
282 link_history_t *link_history;
283 void *or_history_p, *link_history_p;
284 double uptime;
285 char buffer[2048];
286 size_t len;
287 int ret;
288 unsigned long upt, downt;
289 routerinfo_t *r;
291 rep_history_clean(now - get_options()->RephistTrackTime);
293 log(severity, LD_GENERAL, "--------------- Dumping history information:");
295 for (orhist_it = digestmap_iter_init(history_map);
296 !digestmap_iter_done(orhist_it);
297 orhist_it = digestmap_iter_next(history_map,orhist_it)) {
298 digestmap_iter_get(orhist_it, &digest1, &or_history_p);
299 or_history = (or_history_t*) or_history_p;
301 if ((r = router_get_by_digest(digest1)))
302 name1 = r->nickname;
303 else
304 name1 = "(unknown)";
305 base16_encode(hexdigest1, sizeof(hexdigest1), digest1, DIGEST_LEN);
306 update_or_history(or_history, now);
307 upt = or_history->uptime;
308 downt = or_history->downtime;
309 if (upt+downt) {
310 uptime = ((double)upt) / (upt+downt);
311 } else {
312 uptime=1.0;
314 log(severity, LD_GENERAL,
315 "OR %s [%s]: %ld/%ld good connections; uptime %ld/%ld sec (%.2f%%)",
316 name1, hexdigest1,
317 or_history->n_conn_ok, or_history->n_conn_fail+or_history->n_conn_ok,
318 upt, upt+downt, uptime*100.0);
320 if (!digestmap_isempty(or_history->link_history_map)) {
321 strlcpy(buffer, " Extend attempts: ", sizeof(buffer));
322 len = strlen(buffer);
323 for (lhist_it = digestmap_iter_init(or_history->link_history_map);
324 !digestmap_iter_done(lhist_it);
325 lhist_it = digestmap_iter_next(or_history->link_history_map,
326 lhist_it)) {
327 digestmap_iter_get(lhist_it, &digest2, &link_history_p);
328 if ((r = router_get_by_digest(digest2)))
329 name2 = r->nickname;
330 else
331 name2 = "(unknown)";
333 link_history = (link_history_t*) link_history_p;
335 ret = tor_snprintf(buffer+len, 2048-len, "%s(%ld/%ld); ", name2,
336 link_history->n_extend_ok,
337 link_history->n_extend_ok+link_history->n_extend_fail);
338 if (ret<0)
339 break;
340 else
341 len += ret;
343 log(severity, LD_GENERAL, "%s", buffer);
348 /** Remove history info for routers/links that haven't changed since
349 * <b>before</b> */
350 void
351 rep_history_clean(time_t before)
353 or_history_t *or_history;
354 link_history_t *link_history;
355 void *or_history_p, *link_history_p;
356 digestmap_iter_t *orhist_it, *lhist_it;
357 const char *d1, *d2;
359 orhist_it = digestmap_iter_init(history_map);
360 while (!digestmap_iter_done(orhist_it)) {
361 digestmap_iter_get(orhist_it, &d1, &or_history_p);
362 or_history = or_history_p;
363 if (or_history->changed < before) {
364 orhist_it = digestmap_iter_next_rmv(history_map, orhist_it);
365 free_or_history(or_history);
366 continue;
368 for (lhist_it = digestmap_iter_init(or_history->link_history_map);
369 !digestmap_iter_done(lhist_it); ) {
370 digestmap_iter_get(lhist_it, &d2, &link_history_p);
371 link_history = link_history_p;
372 if (link_history->changed < before) {
373 lhist_it = digestmap_iter_next_rmv(or_history->link_history_map,
374 lhist_it);
375 rephist_total_alloc -= sizeof(link_history_t);
376 tor_free(link_history);
377 continue;
379 lhist_it = digestmap_iter_next(or_history->link_history_map,lhist_it);
381 orhist_it = digestmap_iter_next(history_map, orhist_it);
385 #define NUM_SECS_ROLLING_MEASURE 10
386 #define NUM_SECS_BW_SUM_IS_VALID (24*60*60) /* one day */
387 #define NUM_SECS_BW_SUM_INTERVAL (15*60)
388 #define NUM_TOTALS (NUM_SECS_BW_SUM_IS_VALID/NUM_SECS_BW_SUM_INTERVAL)
391 * Structure to track bandwidth use, and remember the maxima for a given
392 * time period.
394 typedef struct bw_array_t {
395 /** Observation array: Total number of bytes transferred in each of the last
396 * NUM_SECS_ROLLING_MEASURE seconds. This is used as a circular array. */
397 uint64_t obs[NUM_SECS_ROLLING_MEASURE];
398 int cur_obs_idx; /**< Current position in obs. */
399 time_t cur_obs_time; /**< Time represented in obs[cur_obs_idx] */
400 uint64_t total_obs; /**< Total for all members of obs except
401 * obs[cur_obs_idx] */
402 uint64_t max_total; /**< Largest value that total_obs has taken on in the
403 * current period. */
404 uint64_t total_in_period; /**< Total bytes transferred in the current
405 * period. */
407 /** When does the next period begin? */
408 time_t next_period;
409 /** Where in 'maxima' should the maximum bandwidth usage for the current
410 * period be stored? */
411 int next_max_idx;
412 /** How many values in maxima/totals have been set ever? */
413 int num_maxes_set;
414 /** Circular array of the maximum
415 * bandwidth-per-NUM_SECS_ROLLING_MEASURE usage for the last
416 * NUM_TOTALS periods */
417 uint64_t maxima[NUM_TOTALS];
418 /** Circular array of the total bandwidth usage for the last NUM_TOTALS
419 * periods */
420 uint64_t totals[NUM_TOTALS];
421 } bw_array_t;
423 /** Shift the current period of b forward by one.
425 static void
426 commit_max(bw_array_t *b)
428 /* Store total from current period. */
429 b->totals[b->next_max_idx] = b->total_in_period;
430 /* Store maximum from current period. */
431 b->maxima[b->next_max_idx++] = b->max_total;
432 /* Advance next_period and next_max_idx */
433 b->next_period += NUM_SECS_BW_SUM_INTERVAL;
434 if (b->next_max_idx == NUM_TOTALS)
435 b->next_max_idx = 0;
436 if (b->num_maxes_set < NUM_TOTALS)
437 ++b->num_maxes_set;
438 /* Reset max_total. */
439 b->max_total = 0;
440 /* Reset total_in_period. */
441 b->total_in_period = 0;
444 /** Shift the current observation time of 'b' forward by one second.
446 static INLINE void
447 advance_obs(bw_array_t *b)
449 int nextidx;
450 uint64_t total;
452 /* Calculate the total bandwidth for the last NUM_SECS_ROLLING_MEASURE
453 * seconds; adjust max_total as needed.*/
454 total = b->total_obs + b->obs[b->cur_obs_idx];
455 if (total > b->max_total)
456 b->max_total = total;
458 nextidx = b->cur_obs_idx+1;
459 if (nextidx == NUM_SECS_ROLLING_MEASURE)
460 nextidx = 0;
462 b->total_obs = total - b->obs[nextidx];
463 b->obs[nextidx]=0;
464 b->cur_obs_idx = nextidx;
466 if (++b->cur_obs_time >= b->next_period)
467 commit_max(b);
470 /** Add 'n' bytes to the number of bytes in b for second 'when'.
472 static INLINE void
473 add_obs(bw_array_t *b, time_t when, uint64_t n)
475 /* Don't record data in the past. */
476 if (when<b->cur_obs_time)
477 return;
478 /* If we're currently adding observations for an earlier second than
479 * 'when', advance b->cur_obs_time and b->cur_obs_idx by an
480 * appropriate number of seconds, and do all the other housekeeping */
481 while (when>b->cur_obs_time)
482 advance_obs(b);
484 b->obs[b->cur_obs_idx] += n;
485 b->total_in_period += n;
488 /** Allocate, initialize, and return a new bw_array.
490 static bw_array_t *
491 bw_array_new(void)
493 bw_array_t *b;
494 time_t start;
495 b = tor_malloc_zero(sizeof(bw_array_t));
496 rephist_total_alloc += sizeof(bw_array_t);
497 start = time(NULL);
498 b->cur_obs_time = start;
499 b->next_period = start + NUM_SECS_BW_SUM_INTERVAL;
500 return b;
503 static bw_array_t *read_array = NULL;
504 static bw_array_t *write_array = NULL;
506 /** Set up read_array and write_array
508 static void
509 bw_arrays_init(void)
511 read_array = bw_array_new();
512 write_array = bw_array_new();
515 /** We read <b>num_bytes</b> more bytes in second <b>when</b>.
517 * Add num_bytes to the current running total for <b>when</b>.
519 * <b>when</b> can go back to time, but it's safe to ignore calls
520 * earlier than the latest <b>when</b> you've heard of.
522 void
523 rep_hist_note_bytes_written(int num_bytes, time_t when)
525 /* Maybe a circular array for recent seconds, and step to a new point
526 * every time a new second shows up. Or simpler is to just to have
527 * a normal array and push down each item every second; it's short.
529 /* When a new second has rolled over, compute the sum of the bytes we've
530 * seen over when-1 to when-1-NUM_SECS_ROLLING_MEASURE, and stick it
531 * somewhere. See rep_hist_bandwidth_assess() below.
533 add_obs(write_array, when, num_bytes);
536 /** We wrote <b>num_bytes</b> more bytes in second <b>when</b>.
537 * (like rep_hist_note_bytes_written() above)
539 void
540 rep_hist_note_bytes_read(int num_bytes, time_t when)
542 /* if we're smart, we can make this func and the one above share code */
543 add_obs(read_array, when, num_bytes);
546 /** Helper: Return the largest value in b->maxima. (This is equal to the
547 * most bandwidth used in any NUM_SECS_ROLLING_MEASURE period for the last
548 * NUM_SECS_BW_SUM_IS_VALID seconds.)
550 static uint64_t
551 find_largest_max(bw_array_t *b)
553 int i;
554 uint64_t max;
555 max=0;
556 for (i=0; i<NUM_TOTALS; ++i) {
557 if (b->maxima[i]>max)
558 max = b->maxima[i];
560 return max;
564 * Find the largest sums in the past NUM_SECS_BW_SUM_IS_VALID (roughly)
565 * seconds. Find one sum for reading and one for writing. They don't have
566 * to be at the same time).
568 * Return the smaller of these sums, divided by NUM_SECS_ROLLING_MEASURE.
571 rep_hist_bandwidth_assess(void)
573 uint64_t w,r;
574 r = find_largest_max(read_array);
575 w = find_largest_max(write_array);
576 if (r>w)
577 return (int)(w/(double)NUM_SECS_ROLLING_MEASURE);
578 else
579 return (int)(r/(double)NUM_SECS_ROLLING_MEASURE);
581 return 0;
585 * Print the bandwidth history of b (either read_array or write_array)
586 * into the buffer pointed to by buf. The format is simply comma
587 * separated numbers, from oldest to newest.
589 * It returns the number of bytes written.
591 static size_t
592 rep_hist_fill_bandwidth_history(char *buf, size_t len, bw_array_t *b)
594 char *cp = buf;
595 int i, n;
597 if (b->num_maxes_set <= b->next_max_idx) {
598 /* We haven't been through the circular array yet; time starts at i=0.*/
599 i = 0;
600 } else {
601 /* We've been around the array at least once. The next i to be
602 overwritten is the oldest. */
603 i = b->next_max_idx;
606 for (n=0; n<b->num_maxes_set; ++n,++i) {
607 while (i >= NUM_TOTALS) i -= NUM_TOTALS;
608 if (n==(b->num_maxes_set-1))
609 tor_snprintf(cp, len-(cp-buf), U64_FORMAT,
610 U64_PRINTF_ARG(b->totals[i]));
611 else
612 tor_snprintf(cp, len-(cp-buf), U64_FORMAT",",
613 U64_PRINTF_ARG(b->totals[i]));
614 cp += strlen(cp);
616 return cp-buf;
620 * Allocate and return lines for representing this server's bandwidth
621 * history in its descriptor.
623 char *
624 rep_hist_get_bandwidth_lines(void)
626 char *buf, *cp;
627 char t[ISO_TIME_LEN+1];
628 int r;
629 bw_array_t *b;
630 size_t len;
632 /* opt (read|write)-history yyyy-mm-dd HH:MM:SS (n s) n,n,n,n,n... */
633 len = (60+20*NUM_TOTALS)*2;
634 buf = tor_malloc_zero(len);
635 cp = buf;
636 for (r=0;r<2;++r) {
637 b = r?read_array:write_array;
638 tor_assert(b);
639 format_iso_time(t, b->next_period-NUM_SECS_BW_SUM_INTERVAL);
640 tor_snprintf(cp, len-(cp-buf), "opt %s %s (%d s) ",
641 r ? "read-history" : "write-history", t,
642 NUM_SECS_BW_SUM_INTERVAL);
643 cp += strlen(cp);
644 cp += rep_hist_fill_bandwidth_history(cp, len-(cp-buf), b);
645 strlcat(cp, "\n", len-(cp-buf));
646 ++cp;
648 return buf;
651 /** Update <b>state</b> with the newest bandwidth history. */
652 void
653 rep_hist_update_state(or_state_t *state)
655 int len, r;
656 char *buf, *cp;
657 smartlist_t **s_values;
658 time_t *s_begins;
659 int *s_interval;
660 bw_array_t *b;
662 len = 20*NUM_TOTALS+1;
663 buf = tor_malloc_zero(len);
665 for (r=0;r<2;++r) {
666 b = r?read_array:write_array;
667 s_begins = r?&state->BWHistoryReadEnds :&state->BWHistoryWriteEnds;
668 s_interval= r?&state->BWHistoryReadInterval:&state->BWHistoryWriteInterval;
669 s_values = r?&state->BWHistoryReadValues :&state->BWHistoryWriteValues;
671 *s_begins = b->next_period;
672 *s_interval = NUM_SECS_BW_SUM_INTERVAL;
673 if (*s_values) {
674 SMARTLIST_FOREACH(*s_values, char *, cp, tor_free(cp));
675 smartlist_free(*s_values);
677 cp = buf;
678 cp += rep_hist_fill_bandwidth_history(cp, len, b);
679 tor_snprintf(cp, len-(cp-buf), cp == buf ? U64_FORMAT : ","U64_FORMAT,
680 U64_PRINTF_ARG(b->total_in_period));
681 *s_values = smartlist_create();
682 if (server_mode(get_options()))
683 smartlist_split_string(*s_values, buf, ",", SPLIT_SKIP_SPACE, 0);
685 tor_free(buf);
686 state->dirty = 1;
689 /** Set bandwidth history from our saved state.
692 rep_hist_load_state(or_state_t *state, const char **err)
694 time_t s_begins, start;
695 time_t now = time(NULL);
696 uint64_t v;
697 int r,i,ok;
698 int all_ok = 1;
699 int s_interval;
700 smartlist_t *s_values;
701 bw_array_t *b;
703 /* Assert they already have been malloced */
704 tor_assert(read_array && write_array);
706 for (r=0;r<2;++r) {
707 b = r?read_array:write_array;
708 s_begins = r?state->BWHistoryReadEnds:state->BWHistoryWriteEnds;
709 s_interval = r?state->BWHistoryReadInterval:state->BWHistoryWriteInterval;
710 s_values = r?state->BWHistoryReadValues:state->BWHistoryWriteValues;
711 if (s_values && s_begins >= now - NUM_SECS_BW_SUM_INTERVAL*NUM_TOTALS) {
712 start = s_begins - s_interval*(smartlist_len(s_values));
714 b->cur_obs_time = start;
715 b->next_period = start + NUM_SECS_BW_SUM_INTERVAL;
716 SMARTLIST_FOREACH(s_values, char *, cp, {
717 v = tor_parse_uint64(cp, 10, 0, UINT64_MAX, &ok, NULL);
718 if (!ok) {
719 all_ok=0;
720 notice(LD_GENERAL, "Could not parse '%s' into a number.'", cp);
722 add_obs(b, start, v);
723 start += NUM_SECS_BW_SUM_INTERVAL;
727 /* Clean up maxima and observed */
728 /* Do we really want to zero this for the purpose of max capacity? */
729 for (i=0; i<NUM_SECS_ROLLING_MEASURE; ++i) {
730 b->obs[i] = 0;
732 b->total_obs = 0;
733 for (i=0; i<NUM_TOTALS; ++i) {
734 b->maxima[i] = 0;
736 b->max_total = 0;
739 if (!all_ok) {
740 if (err)
741 *err = "Parsing of bandwidth history values failed";
742 /* and create fresh arrays */
743 tor_free(read_array);
744 tor_free(write_array);
745 read_array = bw_array_new();
746 write_array = bw_array_new();
747 return -1;
749 return 0;
752 /** A list of port numbers that have been used recently. */
753 static smartlist_t *predicted_ports_list=NULL;
754 /** The corresponding most recently used time for each port. */
755 static smartlist_t *predicted_ports_times=NULL;
757 /** DOCDOC */
758 static void
759 add_predicted_port(uint16_t port, time_t now)
761 /* XXXX we could just use uintptr_t here, I think. */
762 uint16_t *tmp_port = tor_malloc(sizeof(uint16_t));
763 time_t *tmp_time = tor_malloc(sizeof(time_t));
764 *tmp_port = port;
765 *tmp_time = now;
766 rephist_total_alloc += sizeof(uint16_t) + sizeof(time_t);
767 smartlist_add(predicted_ports_list, tmp_port);
768 smartlist_add(predicted_ports_times, tmp_time);
771 /** DOCDOC */
772 static void
773 predicted_ports_init(void)
775 predicted_ports_list = smartlist_create();
776 predicted_ports_times = smartlist_create();
777 add_predicted_port(80, time(NULL)); /* add one to kickstart us */
780 /** DOCDOC */
781 static void
782 predicted_ports_free(void)
784 rephist_total_alloc -= smartlist_len(predicted_ports_list)*sizeof(uint16_t);
785 SMARTLIST_FOREACH(predicted_ports_list, char *, cp, tor_free(cp));
786 smartlist_free(predicted_ports_list);
787 rephist_total_alloc -= smartlist_len(predicted_ports_times)*sizeof(time_t);
788 SMARTLIST_FOREACH(predicted_ports_times, char *, cp, tor_free(cp));
789 smartlist_free(predicted_ports_times);
792 /** Remember that <b>port</b> has been asked for as of time <b>now</b>.
793 * This is used for predicting what sorts of streams we'll make in the
794 * future and making exit circuits to anticipate that.
796 void
797 rep_hist_note_used_port(uint16_t port, time_t now)
799 int i;
800 uint16_t *tmp_port;
801 time_t *tmp_time;
803 tor_assert(predicted_ports_list);
804 tor_assert(predicted_ports_times);
806 if (!port) /* record nothing */
807 return;
809 for (i = 0; i < smartlist_len(predicted_ports_list); ++i) {
810 tmp_port = smartlist_get(predicted_ports_list, i);
811 tmp_time = smartlist_get(predicted_ports_times, i);
812 if (*tmp_port == port) {
813 *tmp_time = now;
814 return;
817 /* it's not there yet; we need to add it */
818 add_predicted_port(port, now);
821 #define PREDICTED_CIRCS_RELEVANCE_TIME (3600) /* 1 hour */
823 /** Return a pointer to the list of port numbers that
824 * are likely to be asked for in the near future.
826 * The caller promises not to mess with it.
828 smartlist_t *
829 rep_hist_get_predicted_ports(time_t now)
831 int i;
832 uint16_t *tmp_port;
833 time_t *tmp_time;
835 tor_assert(predicted_ports_list);
836 tor_assert(predicted_ports_times);
838 /* clean out obsolete entries */
839 for (i = 0; i < smartlist_len(predicted_ports_list); ++i) {
840 tmp_time = smartlist_get(predicted_ports_times, i);
841 if (*tmp_time + PREDICTED_CIRCS_RELEVANCE_TIME < now) {
842 tmp_port = smartlist_get(predicted_ports_list, i);
843 debug(LD_CIRC, "Expiring predicted port %d", *tmp_port);
844 smartlist_del(predicted_ports_list, i);
845 smartlist_del(predicted_ports_times, i);
846 rephist_total_alloc -= sizeof(uint16_t)+sizeof(time_t);
847 tor_free(tmp_port);
848 tor_free(tmp_time);
849 i--;
852 return predicted_ports_list;
855 /** The user asked us to do a resolve. Rather than keeping track of
856 * timings and such of resolves, we fake it for now by making treating
857 * it the same way as a connection to port 80. This way we will continue
858 * to have circuits lying around if the user only uses Tor for resolves.
860 void
861 rep_hist_note_used_resolve(time_t now)
863 rep_hist_note_used_port(80, now);
866 #if 0
868 rep_hist_get_predicted_resolve(time_t now)
870 return 0;
872 #endif
874 /** The last time at which we needed an internal circ. */
875 static time_t predicted_internal_time = 0;
876 /** The last time we needed an internal circ with good uptime. */
877 static time_t predicted_internal_uptime_time = 0;
878 /** The last time we needed an internal circ with good capacity. */
879 static time_t predicted_internal_capacity_time = 0;
881 /** Remember that we used an internal circ at time <b>now</b>. */
882 void
883 rep_hist_note_used_internal(time_t now, int need_uptime, int need_capacity)
885 predicted_internal_time = now;
886 if (need_uptime)
887 predicted_internal_uptime_time = now;
888 if (need_capacity)
889 predicted_internal_capacity_time = now;
892 /** Return 1 if we've used an internal circ recently; else return 0. */
894 rep_hist_get_predicted_internal(time_t now, int *need_uptime,
895 int *need_capacity)
897 if (!predicted_internal_time) { /* initialize it */
898 predicted_internal_time = now;
899 predicted_internal_uptime_time = now;
900 predicted_internal_capacity_time = now;
902 if (predicted_internal_time + PREDICTED_CIRCS_RELEVANCE_TIME < now)
903 return 0; /* too long ago */
904 if (predicted_internal_uptime_time + PREDICTED_CIRCS_RELEVANCE_TIME < now)
905 *need_uptime = 1;
906 if (predicted_internal_capacity_time + PREDICTED_CIRCS_RELEVANCE_TIME < now)
907 *need_capacity = 1;
908 return 1;
911 /** Free all storage held by the OR/link history caches, by the
912 * bandwidth history arrays, or by the port history. */
913 void
914 rep_hist_free_all(void)
916 digestmap_free(history_map, free_or_history);
917 tor_free(read_array);
918 tor_free(write_array);
919 predicted_ports_free();