1
2/*--------------------------------------------------------------------*/
3/*--- A simple debuginfo server for Valgrind.                      ---*/
4/*---                                         valgrind-di-server.c ---*/
5/*--------------------------------------------------------------------*/
6
7/* To build for an x86_64-linux host:
8      gcc -g -Wall -O -o valgrind-di-server \
9         auxprogs/valgrind-di-server.c -Icoregrind -Iinclude \
10         -IVEX/pub -DVGO_linux -DVGA_amd64
11
12   To build for an x86 (32-bit) host
13      The same, except change -DVGA_amd64 to -DVGA_x86
14*/
15
16/*
17   This file is part of Valgrind, a dynamic binary instrumentation
18   framework.
19
20   Copyright (C) 2013-2013 Mozilla Foundation
21
22   This program is free software; you can redistribute it and/or
23   modify it under the terms of the GNU General Public License as
24   published by the Free Software Foundation; either version 2 of the
25   License, or (at your option) any later version.
26
27   This program is distributed in the hope that it will be useful, but
28   WITHOUT ANY WARRANTY; without even the implied warranty of
29   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
30   General Public License for more details.
31
32   You should have received a copy of the GNU General Public License
33   along with this program; if not, write to the Free Software
34   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
35   02111-1307, USA.
36
37   The GNU General Public License is contained in the file COPYING.
38*/
39
40/* Contributed by Julian Seward <jseward@acm.org> */
41
42/* This code works (just), but it's a mess.  Cleanups (also for
43   coregrind/m_debuginfo/image.c):
44
45   * Build this file for the host arch, not the target.  But how?
46     Even Tromey had difficulty figuring out how to do that.
47
48   * Change the use of pread w/ fd to FILE*, for the file we're
49     serving.  Or, at least, put a loop around the pread uses
50     so that it works correctly in the case where pread reads more
51     than zero but less than we asked for.
52
53   * CRC3 request/response: pass session-IDs back and forth and
54     check them
55
56   * Check that all error cases result in a FAIL frame being returned.
57
58   * image.c: don't assert in cases where a FAIL frame is returned;
59     instead cause the debuginfo reading to fail gracefully.  (Not
60     sure how to do this)
61
62   * Improve diagnostic printing
63
64   * image.c: do we need to do VG_(write_socket) ?  Will it work
65     just to use ordinary VG_(write) ?
66
67   * Both files: document the reason for setting TCP_NODELAY
68
69   * Add a command line argument saying where the served-from
70     directory is -- changes clo_serverpath.
71
72   * Fix up (common up) massive code duplication between client and
73     server.
74
75   * Tidy up the LZO source files; integrate properly in the build
76     system.
77*/
78
79/*---------------------------------------------------------------*/
80
81/* Include valgrind headers before system headers to avoid problems
82   with the system headers #defining things which are used as names
83   of structure members in vki headers. */
84
85#include "pub_core_basics.h"
86#include "pub_core_libcassert.h"    // For VG_BUGS_TO
87#include "pub_core_vki.h"           // Avoids warnings from
88                                    // pub_core_libcfile.h
89#include "pub_core_libcfile.h"      // For VG_CLO_DEFAULT_LOGPORT
90
91/* Needed to get a definition for pread() from unistd.h */
92#define _XOPEN_SOURCE 500
93
94#include <stdio.h>
95#include <unistd.h>
96#include <string.h>
97#include <time.h>
98#include <fcntl.h>
99#include <stdlib.h>
100#include <signal.h>
101#include <sys/poll.h>
102#include <sys/types.h>
103#include <sys/socket.h>
104#include <netinet/in.h>
105#include <sys/stat.h>
106#include <netinet/tcp.h>
107
108#include "../coregrind/m_debuginfo/minilzo.h"
109
110/*---------------------------------------------------------------*/
111
112/* The default allowable number of concurrent connections. */
113#define  M_CONNECTIONS_DEFAULT 50
114/* The maximum allowable number of concurrent connections. */
115#define  M_CONNECTIONS_MAX     5000
116
117/* The maximum allowable number of concurrent connections. */
118unsigned M_CONNECTIONS = 0;
119
120static const char* clo_serverpath = ".";
121
122
123/*---------------------------------------------------------------*/
124
125__attribute__ ((noreturn))
126static void panic ( const char* str )
127{
128   fprintf(stderr,
129           "\nvalgrind-di-server: the "
130           "'impossible' happened:\n   %s\n", str);
131   fprintf(stderr,
132           "Please report this bug at: %s\n\n", VG_BUGS_TO);
133   exit(1);
134}
135
136__attribute__ ((noreturn))
137static void my_assert_fail ( const char* expr, const char* file, int line, const char* fn )
138{
139   fprintf(stderr,
140           "\nvalgrind-di-server: %s:%d (%s): Assertion '%s' failed.\n",
141           file, line, fn, expr );
142   fprintf(stderr,
143           "Please report this bug at: %s\n\n", VG_BUGS_TO);
144   exit(1);
145}
146
147#undef assert
148
149#define assert(expr)                                             \
150  ((void) ((expr) ? 0 :					         \
151	   (my_assert_fail (VG_STRINGIFY(expr),	                 \
152                            __FILE__, __LINE__,                  \
153                            __PRETTY_FUNCTION__), 0)))
154
155
156/*---------------------------------------------------------------*/
157
158/* Allocate some memory. Return iff successful. */
159static void *my_malloc(size_t amount)
160{
161  void *p = malloc(amount ?: 1);
162
163  if (p == NULL) {
164     fprintf(stderr, "Memory allocation failed; cannot continue.\n");
165     exit(1);
166  }
167  return p;
168}
169
170/*---------------------------------------------------------------*/
171
172/* Holds the state that we need to track, for each connection. */
173typedef
174   struct {
175      // is this entry in use?
176      Bool in_use;
177      // socket descriptor to communicate with client.  Initialised as
178      // soon as this entry is created.
179      int  conn_sd;
180      // fd for the file that we are connected to.  Zero if not
181      // currently connected to any file.
182      int   file_fd;
183      ULong file_size;
184      // Session ID
185      ULong session_id;
186      // How many bytes and chunks sent?
187      ULong stats_n_rdok_frames;
188      ULong stats_n_read_unz_bytes; // bytes via READ (uncompressed)
189      ULong stats_n_read_z_bytes;   // bytes via READ (compressed)
190   }
191   ConnState;
192
193/* The state itself. */
194static int       conn_count = 0;
195static ConnState *conn_state;
196
197/* Issues unique session ID values. */
198static ULong next_session_id = 1;
199
200
201/*---------------------------------------------------------------*/
202
203// Code that is duplicated with the client :-(
204
205/* The following Adler-32 checksum code is taken from zlib-1.2.3, which
206   has the following copyright notice. */
207/*
208Copyright notice:
209
210 (C) 1995-2004 Jean-loup Gailly and Mark Adler
211
212  This software is provided 'as-is', without any express or implied
213  warranty.  In no event will the authors be held liable for any damages
214  arising from the use of this software.
215
216  Permission is granted to anyone to use this software for any purpose,
217  including commercial applications, and to alter it and redistribute it
218  freely, subject to the following restrictions:
219
220  1. The origin of this software must not be misrepresented; you must not
221     claim that you wrote the original software. If you use this software
222     in a product, an acknowledgment in the product documentation would be
223     appreciated but is not required.
224  2. Altered source versions must be plainly marked as such, and must not be
225     misrepresented as being the original software.
226  3. This notice may not be removed or altered from any source distribution.
227
228  Jean-loup Gailly        Mark Adler
229  jloup@gzip.org          madler@alumni.caltech.edu
230
231If you use the zlib library in a product, we would appreciate *not*
232receiving lengthy legal documents to sign. The sources are provided
233for free but without warranty of any kind.  The library has been
234entirely written by Jean-loup Gailly and Mark Adler; it does not
235include third-party code.
236
237If you redistribute modified sources, we would appreciate that you include
238in the file ChangeLog history information documenting your changes. Please
239read the FAQ for more information on the distribution of modified source
240versions.
241*/
242
243/* Update a running Adler-32 checksum with the bytes buf[0..len-1] and
244   return the updated checksum. If buf is NULL, this function returns
245   the required initial value for the checksum. An Adler-32 checksum is
246   almost as reliable as a CRC32 but can be computed much faster. */
247static
248UInt adler32( UInt adler, const UChar* buf, UInt len )
249{
250#  define BASE 65521UL    /* largest prime smaller than 65536 */
251#  define NMAX 5552
252   /* NMAX is the largest n such that
253      255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
254
255#  define DO1(buf,i)  {adler += (buf)[i]; sum2 += adler;}
256#  define DO2(buf,i)  DO1(buf,i); DO1(buf,i+1);
257#  define DO4(buf,i)  DO2(buf,i); DO2(buf,i+2);
258#  define DO8(buf,i)  DO4(buf,i); DO4(buf,i+4);
259#  define DO16(buf)   DO8(buf,0); DO8(buf,8);
260
261   /* The zlib sources recommend this definition of MOD if the
262      processor cannot do integer division in hardware. */
263#  define MOD(a) \
264      do { \
265          if (a >= (BASE << 16)) a -= (BASE << 16); \
266          if (a >= (BASE << 15)) a -= (BASE << 15); \
267          if (a >= (BASE << 14)) a -= (BASE << 14); \
268          if (a >= (BASE << 13)) a -= (BASE << 13); \
269          if (a >= (BASE << 12)) a -= (BASE << 12); \
270          if (a >= (BASE << 11)) a -= (BASE << 11); \
271          if (a >= (BASE << 10)) a -= (BASE << 10); \
272          if (a >= (BASE << 9)) a -= (BASE << 9); \
273          if (a >= (BASE << 8)) a -= (BASE << 8); \
274          if (a >= (BASE << 7)) a -= (BASE << 7); \
275          if (a >= (BASE << 6)) a -= (BASE << 6); \
276          if (a >= (BASE << 5)) a -= (BASE << 5); \
277          if (a >= (BASE << 4)) a -= (BASE << 4); \
278          if (a >= (BASE << 3)) a -= (BASE << 3); \
279          if (a >= (BASE << 2)) a -= (BASE << 2); \
280          if (a >= (BASE << 1)) a -= (BASE << 1); \
281          if (a >= BASE) a -= BASE; \
282      } while (0)
283#  define MOD4(a) \
284      do { \
285          if (a >= (BASE << 4)) a -= (BASE << 4); \
286          if (a >= (BASE << 3)) a -= (BASE << 3); \
287          if (a >= (BASE << 2)) a -= (BASE << 2); \
288          if (a >= (BASE << 1)) a -= (BASE << 1); \
289          if (a >= BASE) a -= BASE; \
290      } while (0)
291
292    UInt sum2;
293    UInt n;
294
295    /* split Adler-32 into component sums */
296    sum2 = (adler >> 16) & 0xffff;
297    adler &= 0xffff;
298
299    /* in case user likes doing a byte at a time, keep it fast */
300    if (len == 1) {
301        adler += buf[0];
302        if (adler >= BASE)
303            adler -= BASE;
304        sum2 += adler;
305        if (sum2 >= BASE)
306            sum2 -= BASE;
307        return adler | (sum2 << 16);
308    }
309
310    /* initial Adler-32 value (deferred check for len == 1 speed) */
311    if (buf == NULL)
312        return 1L;
313
314    /* in case short lengths are provided, keep it somewhat fast */
315    if (len < 16) {
316        while (len--) {
317            adler += *buf++;
318            sum2 += adler;
319        }
320        if (adler >= BASE)
321            adler -= BASE;
322        MOD4(sum2);             /* only added so many BASE's */
323        return adler | (sum2 << 16);
324    }
325
326    /* do length NMAX blocks -- requires just one modulo operation */
327    while (len >= NMAX) {
328        len -= NMAX;
329        n = NMAX / 16;          /* NMAX is divisible by 16 */
330        do {
331            DO16(buf);          /* 16 sums unrolled */
332            buf += 16;
333        } while (--n);
334        MOD(adler);
335        MOD(sum2);
336    }
337
338    /* do remaining bytes (less than NMAX, still just one modulo) */
339    if (len) {                  /* avoid modulos if none remaining */
340        while (len >= 16) {
341            len -= 16;
342            DO16(buf);
343            buf += 16;
344        }
345        while (len--) {
346            adler += *buf++;
347            sum2 += adler;
348        }
349        MOD(adler);
350        MOD(sum2);
351    }
352
353    /* return recombined sums */
354    return adler | (sum2 << 16);
355
356#  undef MOD4
357#  undef MOD
358#  undef DO16
359#  undef DO8
360#  undef DO4
361#  undef DO2
362#  undef DO1
363#  undef NMAX
364#  undef BASE
365}
366
367
368/* A frame.  The first 4 bytes of |data| give the kind of the frame,
369   and the rest of it is kind-specific data. */
370typedef  struct { UChar* data; SizeT n_data; }  Frame;
371
372
373static void write_UInt_le ( /*OUT*/UChar* dst, UInt n )
374{
375   Int i;
376   for (i = 0; i <= 3; i++) {
377      dst[i] = (UChar)(n & 0xFF);
378      n >>= 8;
379   }
380}
381
382static UInt read_UInt_le ( UChar* src )
383{
384   UInt r = 0;
385   Int i;
386   for (i = 3; i >= 0; i--) {
387      r <<= 8;
388      r += (UInt)src[i];
389   }
390   return r;
391}
392
393static void write_ULong_le ( /*OUT*/UChar* dst, ULong n )
394{
395   Int i;
396   for (i = 0; i <= 7; i++) {
397      dst[i] = (UChar)(n & 0xFF);
398      n >>= 8;
399   }
400}
401
402static ULong read_ULong_le ( UChar* src )
403{
404   ULong r = 0;
405   Int i;
406   for (i = 7; i >= 0; i--) {
407      r <<= 8;
408      r += (ULong)src[i];
409   }
410   return r;
411}
412
413static Frame* mk_Frame_asciiz ( const char* tag, const char* str )
414{
415   assert(strlen(tag) == 4);
416   Frame* f = calloc(sizeof(Frame), 1);
417   size_t n_str = strlen(str);
418   f->n_data = 4 + n_str + 1;
419   f->data = calloc(f->n_data, 1);
420   memcpy(&f->data[0], tag, 4);
421   memcpy(&f->data[4], str, n_str);
422   assert(f->data[4 + n_str] == 0);
423   return f;
424}
425
426static Bool parse_Frame_noargs ( Frame* fr, const HChar* tag )
427{
428   assert(strlen(tag) == 4);
429   if (!fr || !fr->data) return False;
430   if (fr->n_data < 4) return False;
431   if (memcmp(&fr->data[0], tag, 4) != 0) return False;
432   if (fr->n_data != 4) return False;
433   return True;
434}
435
436static Bool parse_Frame_asciiz ( Frame* fr, const HChar* tag,
437                                 /*OUT*/UChar** str )
438{
439   assert(strlen(tag) == 4);
440   if (!fr || !fr->data) return False;
441   if (fr->n_data < 4) return False;
442   if (memcmp(&fr->data[0], tag, 4) != 0) return False;
443   if (fr->n_data < 5) return False; // else there isn't even enough
444                                     // space for the terminating zero
445   /* Find the terminating zero and ensure it's right at the end
446      of the data.  If not, the frame is malformed. */
447   SizeT i = 4;
448   while (True) {
449      if (i >= fr->n_data) break;
450      if (fr->data[i] == 0) break;
451      i++;
452   }
453   assert(i <= fr->n_data);
454   if (i == fr->n_data-1 && fr->data[i] == 0) {
455      *str = &fr->data[4];
456      return True;
457   } else {
458      return False;
459   }
460}
461
462static Frame* mk_Frame_le64 ( const HChar* tag, ULong n1 )
463{
464   assert(strlen(tag) == 4);
465   Frame* f = calloc(sizeof(Frame), 1);
466   f->n_data = 4 + 1*8;
467   f->data = calloc(f->n_data, 1);
468   memcpy(&f->data[0], tag, 4);
469   write_ULong_le(&f->data[4 + 0*8], n1);
470   return f;
471}
472
473static Frame* mk_Frame_le64_le64 ( const HChar* tag, ULong n1, ULong n2 )
474{
475   assert(strlen(tag) == 4);
476   Frame* f = calloc(sizeof(Frame), 1);
477   f->n_data = 4 + 2*8;
478   f->data = calloc(f->n_data, 1);
479   memcpy(&f->data[0], tag, 4);
480   write_ULong_le(&f->data[4 + 0*8], n1);
481   write_ULong_le(&f->data[4 + 1*8], n2);
482   return f;
483}
484
485static Bool parse_Frame_le64_le64_le64 ( Frame* fr, const HChar* tag,
486                                         /*OUT*/ULong* n1, /*OUT*/ULong* n2,
487                                         /*OUT*/ULong* n3 )
488{
489   assert(strlen(tag) == 4);
490   if (!fr || !fr->data) return False;
491   if (fr->n_data < 4) return False;
492   if (memcmp(&fr->data[0], tag, 4) != 0) return False;
493   if (fr->n_data != 4 + 3*8) return False;
494   *n1 = read_ULong_le(&fr->data[4 + 0*8]);
495   *n2 = read_ULong_le(&fr->data[4 + 1*8]);
496   *n3 = read_ULong_le(&fr->data[4 + 2*8]);
497   return True;
498}
499
500static Frame* mk_Frame_le64_le64_le64_bytes (
501                 const HChar* tag,
502                 ULong n1, ULong n2, ULong n3, ULong n_data,
503                 /*OUT*/UChar** data )
504{
505   assert(strlen(tag) == 4);
506   Frame* f = calloc(sizeof(Frame), 1);
507   f->n_data = 4 + 3*8 + n_data;
508   f->data = calloc(f->n_data, 1);
509   memcpy(&f->data[0], tag, 4);
510   write_ULong_le(&f->data[4 + 0*8], n1);
511   write_ULong_le(&f->data[4 + 1*8], n2);
512   write_ULong_le(&f->data[4 + 2*8], n3);
513   *data = &f->data[4 + 3*8];
514   return f;
515}
516
517static void free_Frame ( Frame* fr )
518{
519   assert(fr && fr->data);
520   free(fr->data);
521   free(fr);
522}
523
524
525static void set_blocking ( int sd )
526{
527   int res;
528   res = fcntl(sd, F_GETFL);
529   res = fcntl(sd, F_SETFL, res & ~O_NONBLOCK);
530   if (res != 0) {
531      perror("fcntl failed");
532      panic("set_blocking");
533   }
534}
535
536
537#if 0
538static void set_nonblocking ( int sd )
539{
540   int res;
541   res = fcntl(sd, F_GETFL);
542   res = fcntl(sd, F_SETFL, res | O_NONBLOCK);
543   if (res != 0) {
544      perror("fcntl failed");
545      panic("set_nonblocking");
546   }
547}
548#endif
549
550
551/* Tries to read 'len' bytes from fd, blocking if necessary.  Assumes
552   fd has been set in blocking mode.  If it returns with the number of
553   bytes read < len, it means that either fd was closed, or there was
554   an error on it. */
555static SizeT my_read ( Int fd, UChar* buf, SizeT len )
556{
557  //set_blocking(fd);
558   SizeT nRead = 0;
559   while (1) {
560      if (nRead == len) return nRead;
561      assert(nRead < len);
562      SizeT nNeeded = len - nRead;
563      assert(nNeeded > 0);
564      SSizeT n = read(fd, &buf[nRead], nNeeded);
565      if (n <= 0) return nRead; /* error or EOF */
566      nRead += n;
567   }
568}
569
570/* Tries to write 'len' bytes to fd, blocking if necessary.  Assumes
571   fd has been set in blocking mode.  If it returns with the number of
572   bytes written < len, it means that either fd was closed, or there was
573   an error on it. */
574static SizeT my_write ( Int fd, UChar* buf, SizeT len )
575{
576  //set_nonblocking(fd);
577   SizeT nWritten = 0;
578   while (1) {
579      if (nWritten == len) return nWritten;
580      assert(nWritten < len);
581      SizeT nStillToDo = len - nWritten;
582      assert(nStillToDo > 0);
583      SSizeT n = write(fd, &buf[nWritten], nStillToDo);
584      if (n < 0) return nWritten; /* error or EOF */
585      nWritten += n;
586   }
587}
588
589
590static UInt calc_gnu_debuglink_crc32(/*OUT*/Bool* ok, int fd, ULong size)
591{
592  static const UInt crc32_table[256] =
593    {
594      0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419,
595      0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4,
596      0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07,
597      0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
598      0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856,
599      0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
600      0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4,
601      0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
602      0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3,
603      0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a,
604      0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599,
605      0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
606      0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190,
607      0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f,
608      0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e,
609      0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
610      0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed,
611      0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
612      0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3,
613      0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
614      0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a,
615      0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5,
616      0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010,
617      0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
618      0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17,
619      0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6,
620      0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615,
621      0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
622      0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344,
623      0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
624      0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a,
625      0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
626      0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1,
627      0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c,
628      0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef,
629      0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
630      0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe,
631      0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31,
632      0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c,
633      0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
634      0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b,
635      0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
636      0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1,
637      0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
638      0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278,
639      0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7,
640      0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66,
641      0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
642      0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605,
643      0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8,
644      0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b,
645      0x2d02ef8d
646    };
647
648      /* Work through the image in 1 KB chunks. */
649      UInt  crc      = 0xFFFFFFFF;
650      ULong img_szB  = size;
651      ULong curr_off = 0;
652      while (1) {
653         assert(curr_off >= 0 && curr_off <= img_szB);
654         if (curr_off == img_szB) break;
655         ULong avail = img_szB - curr_off;
656         assert(avail > 0 && avail <= img_szB);
657         if (avail > 65536) avail = 65536;
658         UChar buf[65536];
659         Int nRead = pread(fd, buf, avail, curr_off);
660         if (nRead <= 0) { /* EOF or error on the file; neither should happen */
661            *ok = False;
662            return 0;
663         }
664         /* this is a kludge .. we should loop around pread and deal
665            with short reads, for whatever reason */
666         assert(nRead == avail);
667         UInt i;
668         for (i = 0; i < (UInt)nRead; i++)
669            crc = crc32_table[(crc ^ buf[i]) & 0xff] ^ (crc >> 8);
670         curr_off += nRead;
671      }
672      *ok = True;
673      return ~crc & 0xFFFFFFFF;
674   }
675
676
677/*---------------------------------------------------------------*/
678
679/* Handle a transaction for conn_state[conn_no].  There is incoming
680   data available; read it and send back an appropriate response.
681   Returns a boolean indicating whether the connection has been
682   closed; in which case this function does all necessary cleanup and
683   leaves conn_state[conn_no] in a not-in-use state. */
684
685static Bool handle_transaction ( int conn_no )
686{
687   Frame* req = NULL; /* the request frame that we receive */
688   Frame* res = NULL; /* the response frame that we send back */
689
690   assert(conn_no >= 0 && conn_no < M_CONNECTIONS);
691   assert(conn_state[conn_no].in_use);
692
693   //printf("SERVER: handle_transaction(%d)\n", conn_no); fflush(stdout);
694
695   Int sd = conn_state[conn_no].conn_sd;
696
697   /* Get a frame out of the channel. */
698   UChar rd_first8[8];  // adler32; length32
699   { Int r = my_read(sd, &rd_first8[0], 8);
700     if (r == 0) goto client_closed_conn;
701     if (r != 8) goto fail;
702   }
703   UInt rd_adler = read_UInt_le(&rd_first8[0]);
704   UInt rd_len   = read_UInt_le(&rd_first8[4]);
705   /* Allocate a Frame to hold the result data, and read into it. */
706   // Reject obviously-insane length fields.
707   if (rd_len > 4*1024*1024) goto fail;
708   assert(req == NULL);
709   req = calloc(sizeof(Frame), 1);
710   req->n_data = rd_len;
711   req->data = calloc(rd_len, 1);
712   if (rd_len > 0) {
713      Int r = my_read(sd, req->data, req->n_data);
714      if (r != rd_len) goto fail;
715   }
716//printf("SERVER: recv %c%c%c%c\n", req->data[0], req->data[1], req->data[2], req->data[3]); fflush(stdout);
717
718   /* Compute the checksum for the received data, and check it. */
719   UInt adler = adler32(0, NULL, 0); // initial value
720   adler = adler32(adler, &rd_first8[4], 4);
721   if (req->n_data > 0)
722      adler = adler32(adler, req->data, req->n_data);
723
724   if (adler/*computed*/ != rd_adler/*expected*/) goto fail;
725
726   /* Now we have a request frame.  Cook up a response. */
727   assert(res == NULL);
728
729   UChar* filename = NULL;
730   ULong req_session_id = 0, req_offset = 0, req_len = 0;
731
732   if (parse_Frame_noargs(req, "VERS")) {
733      res = mk_Frame_asciiz("VEOK", "Valgrind Debuginfo Server, Version 1");
734   }
735   else
736   if (parse_Frame_noargs(req, "CRC3")) {
737      /* FIXME: add a session ID to this request, and check it */
738      if (conn_state[conn_no].file_fd == 0) {
739         res = mk_Frame_asciiz("CRC3", "FAIL: not connected to file");
740      } else {
741         Bool ok    = False;
742         UInt crc32 = calc_gnu_debuglink_crc32(&ok,
743                                               conn_state[conn_no].file_fd,
744                                               conn_state[conn_no].file_size);
745         if (ok) {
746            res = mk_Frame_le64("CROK", (ULong)crc32);
747         } else {
748            res = mk_Frame_asciiz("FAIL", "CRC3: I/O error reading file");
749         }
750      }
751   }
752   else
753   if (parse_Frame_asciiz(req, "OPEN", &filename)) {
754      Bool ok = True;
755      int  fd = 0;
756      if (conn_state[conn_no].file_fd != 0) {
757         res = mk_Frame_asciiz("FAIL", "OPEN: already connected to file");
758         ok = False;
759      }
760      if (ok) {
761         assert(clo_serverpath);
762         fd = open((char*)filename, O_RDONLY);
763         if (fd == -1) {
764            res = mk_Frame_asciiz("FAIL", "OPEN: cannot open file");
765            printf("(%d) SessionID %llu: open failed for \"%s\"\n",
766                   conn_count, conn_state[conn_no].session_id, filename );
767            ok = False;
768         } else {
769            assert(fd > 2);
770         }
771      }
772      if (ok) {
773         struct stat stat_buf;
774         int r = fstat(fd, &stat_buf);
775         if (r != 0) {
776            res = mk_Frame_asciiz("FAIL", "OPEN: cannot stat file");
777            ok = False;
778         }
779         if (ok && stat_buf.st_size == 0) {
780            res = mk_Frame_asciiz("FAIL", "OPEN: file has zero size");
781            ok = False;
782         }
783         if (ok) {
784            conn_state[conn_no].file_fd   = fd;
785            conn_state[conn_no].file_size = stat_buf.st_size;
786            assert(res == NULL);
787            res = mk_Frame_le64_le64("OPOK", conn_state[conn_no].session_id,
788                                             conn_state[conn_no].file_size);
789            printf("(%d) SessionID %llu: open successful for \"%s\"\n",
790                   conn_count, conn_state[conn_no].session_id, filename );
791            fflush(stdout);
792         }
793      }
794   }
795   else
796   if (parse_Frame_le64_le64_le64(req, "READ", &req_session_id,
797                                  &req_offset, &req_len)) {
798      /* Because each new connection is associated with its own socket
799         descriptor and hence with a particular conn_no, the requested
800         session-ID is redundant -- it must be the one associated with
801         this slot.  But check anyway. */
802      Bool ok = True;
803      if (req_session_id != conn_state[conn_no].session_id) {
804         res = mk_Frame_asciiz("FAIL", "READ: invalid session ID");
805         ok = False;
806      }
807      /* Check we're connected to a file, and if so range-check the
808         request. */
809      if (ok && conn_state[conn_no].file_fd == 0) {
810         res = mk_Frame_asciiz("FAIL", "READ: no associated file");
811         ok = False;
812      }
813      if (ok && (req_len == 0 || req_len > 4*1024*1024)) {
814         res = mk_Frame_asciiz("FAIL", "READ: invalid request size");
815         ok = False;
816      }
817      if (ok && req_len + req_offset > conn_state[conn_no].file_size) {
818         res = mk_Frame_asciiz("FAIL", "READ: request exceeds file size");
819         ok = False;
820      }
821      /* Try to read the file. */
822      if (ok) {
823         /* First, allocate a temp buf and read from the file into it. */
824         /* FIXME: what if pread reads short and we have to redo it? */
825         UChar* unzBuf = my_malloc(req_len);
826         size_t nRead = pread(conn_state[conn_no].file_fd,
827                              unzBuf, req_len, req_offset);
828         if (nRead != req_len) {
829            free_Frame(res);
830            res = mk_Frame_asciiz("FAIL", "READ: I/O error reading file");
831            ok = False;
832         }
833         if (ok) {
834            // Now compress it with LZO.  LZO appears to recommend
835            // the worst-case output size as (in_len + in_len / 16 + 67).
836            // Be more conservative here.
837#           define STACK_ALLOC(var,size) \
838               lzo_align_t __LZO_MMODEL \
839                  var [ ((size) \
840                        + (sizeof(lzo_align_t) - 1)) / sizeof(lzo_align_t) ]
841            STACK_ALLOC(wrkmem, LZO1X_1_MEM_COMPRESS);
842#           undef STACK_ALLOC
843            UInt zLenMax = req_len + req_len / 4 + 1024;
844            UChar* zBuf = my_malloc(zLenMax);
845            lzo_uint zLen = zLenMax;
846            Int lzo_rc = lzo1x_1_compress(unzBuf, req_len,
847                                          zBuf, &zLen, wrkmem);
848            if (lzo_rc == LZO_E_OK) {
849              //printf("XXXXX req_len %u  zLen %u\n", (UInt)req_len, (UInt)zLen);
850               assert(zLen <= zLenMax);
851               /* Make a frame to put the results in.  Bytes 24 and
852                  onwards need to be filled from the compressed data,
853                  and 'buf' is set to point to the right bit. */
854               UChar* buf = NULL;
855               res = mk_Frame_le64_le64_le64_bytes
856                 ("RDOK", req_session_id, req_offset, req_len, zLen, &buf);
857               assert(res);
858               assert(buf);
859               memcpy(buf, zBuf, zLen);
860               // Update stats
861               conn_state[conn_no].stats_n_rdok_frames++;
862               conn_state[conn_no].stats_n_read_unz_bytes += req_len;
863               conn_state[conn_no].stats_n_read_z_bytes   += zLen;
864            } else {
865               ok = False;
866               free_Frame(res);
867               res = mk_Frame_asciiz("FAIL", "READ: LZO failed");
868            }
869            free(zBuf);
870         }
871         free(unzBuf);
872      }
873   }
874   else {
875      res = mk_Frame_asciiz("FAIL", "Invalid request frame type");
876   }
877
878   /* All paths through the above should result in an assignment to |res|. */
879   assert(res != NULL);
880
881   /* And send the response frame back to the client. */
882   /* What goes on the wire is:
883         adler(le32) n_data(le32) data[0 .. n_data-1]
884      where the checksum covers n_data as well as data[].
885   */
886   /* The initial Adler-32 value */
887   adler = adler32(0, NULL, 0);
888
889   /* Fold in the length field, encoded as le32. */
890   UChar wr_first8[8];
891   write_UInt_le(&wr_first8[4], res->n_data);
892   adler = adler32(adler, &wr_first8[4], 4);
893   /* Fold in the data values */
894   adler = adler32(adler, res->data, res->n_data);
895   write_UInt_le(&wr_first8[0], adler);
896
897   Int r = my_write(sd, &wr_first8[0], 8);
898   if (r != 8) goto fail;
899   assert(res->n_data >= 4); // else ill formed -- no KIND field
900   r = my_write(sd, res->data, res->n_data);
901   if (r != res->n_data) goto fail;
902
903//printf("SERVER: send %c%c%c%c\n", res->data[0], res->data[1], res->data[2], res->data[3]); fflush(stdout);
904
905   /* So, success. */
906   free_Frame(req);
907   free_Frame(res);
908   return False;  /* "connection still in use" */
909
910   // Is there any difference between these?
911  client_closed_conn:
912  fail:
913   if (conn_state[conn_no].conn_sd > 0)
914      close(conn_state[conn_no].conn_sd);
915   if (conn_state[conn_no].file_fd > 0)
916      close(conn_state[conn_no].file_fd);
917
918   if (conn_state[conn_no].stats_n_rdok_frames > 0) {
919      printf("(%d) SessionID %llu:   sent %llu frames, "
920             "%llu MB (unz), %llu MB (z), ratio %4.2f:1\n",
921             conn_count, conn_state[conn_no].session_id,
922             conn_state[conn_no].stats_n_rdok_frames,
923             conn_state[conn_no].stats_n_read_unz_bytes / 1000000,
924             conn_state[conn_no].stats_n_read_z_bytes / 1000000,
925             (double)conn_state[conn_no].stats_n_read_unz_bytes
926               / (double)conn_state[conn_no].stats_n_read_z_bytes);
927      printf("(%d) SessionID %llu: closed\n",
928             conn_count, conn_state[conn_no].session_id);
929
930      fflush(stdout);
931   }
932
933   memset(&conn_state[conn_no], 0, sizeof(conn_state[conn_no]));
934   if (req) free_Frame(req);
935   if (res) free_Frame(res);
936   return True; /* "connection has been closed" */
937}
938
939
940/*---------------------------------------------------------------*/
941
942
943
944#if 0
945static void copyout ( char* buf, int nbuf )
946{
947   int i;
948   for (i = 0; i < nbuf; i++) {
949      if (buf[i] == '\n') {
950         fprintf(stdout, "\n(%d) ", conn_count);
951      } else {
952         __attribute__((unused)) size_t ignored
953            = fwrite(&buf[i], 1, 1, stdout);
954      }
955   }
956   fflush(stdout);
957}
958
959static int read_from_sd ( int sd )
960{
961   char buf[100];
962   int n;
963
964   set_blocking(sd);
965   n = read(sd, buf, 99);
966   if (n <= 0) return 0; /* closed */
967   copyout(buf, n);
968
969   set_nonblocking(sd);
970   while (1) {
971      n = read(sd, buf, 100);
972      if (n <= 0) return 1; /* not closed */
973      copyout(buf, n);
974   }
975}
976#endif
977
978static void snooze ( void )
979{
980   struct timespec req;
981   req.tv_sec = 0;
982   req.tv_nsec = 200 * 1000 * 1000;
983   nanosleep(&req,NULL);
984}
985
986
987/* returns 0 if negative, or > BOUND or invalid characters were found */
988static int atoi_with_bound ( const char* str, int bound )
989{
990   int n = 0;
991   while (1) {
992      if (*str == 0)
993         break;
994      if (*str < '0' || *str > '9')
995         return 0;
996      n = 10*n + (int)(*str - '0');
997      str++;
998      if (n >= bound)
999         return 0;
1000   }
1001   return n;
1002}
1003
1004
1005/* returns 0 if invalid, else port # */
1006static int atoi_portno ( const char* str )
1007{
1008   int n = atoi_with_bound(str, 65536);
1009
1010   if (n < 1024)
1011      return 0;
1012   return n;
1013}
1014
1015
1016static void usage ( void )
1017{
1018   fprintf(stderr,
1019      "\n"
1020      "usage is:\n"
1021      "\n"
1022      "   valgrind-di-server [--exit-at-zero|-e] [port-number]\n"
1023      "\n"
1024      "   where   --exit-at-zero or -e causes the listener to exit\n"
1025      "           when the number of connections falls back to zero\n"
1026      "           (the default is to keep listening forever)\n"
1027      "\n"
1028      "           --max-connect=INT can be used to increase the maximum\n"
1029      "           number of connected processes (default = %d).\n"
1030      "           INT must be positive and less than %d.\n"
1031      "\n"
1032      "           port-number is the default port on which to listen for\n"
1033      "           connections.  It must be between 1024 and 65535.\n"
1034      "           Current default is %d.\n"
1035      "\n"
1036      ,
1037      M_CONNECTIONS_DEFAULT, M_CONNECTIONS_MAX, VG_CLO_DEFAULT_LOGPORT
1038   );
1039   exit(1);
1040}
1041
1042
1043static void banner ( const char* str )
1044{
1045   time_t t;
1046   t = time(NULL);
1047   printf("valgrind-di-server %s at %s", str, ctime(&t));
1048   fflush(stdout);
1049}
1050
1051
1052static void exit_routine ( void )
1053{
1054   banner("exited");
1055   exit(0);
1056}
1057
1058
1059static void sigint_handler ( int signo )
1060{
1061   exit_routine();
1062}
1063
1064
1065int main (int argc, char** argv)
1066{
1067   int    i, j, res, one;
1068   int    main_sd, new_sd;
1069   socklen_t client_len;
1070   struct sockaddr_in client_addr, server_addr;
1071
1072   char /*bool*/ exit_when_zero = 0;
1073   int           port = VG_CLO_DEFAULT_LOGPORT;
1074
1075   for (i = 1; i < argc; i++) {
1076      if (0==strcmp(argv[i], "--exit-at-zero")
1077          || 0==strcmp(argv[i], "-e")) {
1078         exit_when_zero = 1;
1079      }
1080      else if (0 == strncmp(argv[i], "--max-connect=", 14)) {
1081         M_CONNECTIONS = atoi_with_bound(strchr(argv[i], '=') + 1, 5000);
1082         if (M_CONNECTIONS <= 0 || M_CONNECTIONS > M_CONNECTIONS_MAX)
1083            usage();
1084      }
1085      else
1086      if (atoi_portno(argv[i]) > 0) {
1087         port = atoi_portno(argv[i]);
1088      }
1089      else
1090      usage();
1091   }
1092
1093   if (M_CONNECTIONS == 0)   // nothing specified on command line
1094      M_CONNECTIONS = M_CONNECTIONS_DEFAULT;
1095
1096   conn_state = my_malloc(M_CONNECTIONS * sizeof conn_state[0]);
1097
1098   banner("started");
1099   signal(SIGINT, sigint_handler);
1100
1101   conn_count = 0;
1102   memset(conn_state, 0, M_CONNECTIONS * sizeof conn_state[0]);
1103
1104   /* create socket */
1105   main_sd = socket(AF_INET, SOCK_STREAM, 0);
1106   if (main_sd < 0) {
1107      perror("cannot open socket ");
1108      panic("main -- create socket");
1109   }
1110
1111   /* allow address reuse to avoid "address already in use" errors */
1112
1113   one = 1;
1114   if (setsockopt(main_sd, SOL_SOCKET, SO_REUSEADDR,
1115		  &one, sizeof(one)) < 0) {
1116      perror("cannot enable address reuse ");
1117      panic("main -- enable address reuse");
1118   }
1119
1120   /* bind server port */
1121   server_addr.sin_family      = AF_INET;
1122   server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
1123   server_addr.sin_port        = htons(port);
1124
1125   if (bind(main_sd, (struct sockaddr *) &server_addr,
1126                     sizeof(server_addr) ) < 0) {
1127      perror("cannot bind port ");
1128      panic("main -- bind port");
1129   }
1130
1131   res = listen(main_sd, M_CONNECTIONS);
1132   if (res != 0) {
1133      perror("listen failed ");
1134      panic("main -- listen");
1135   }
1136
1137   Bool do_snooze = False;
1138   while (1) {
1139
1140      if (0 && do_snooze)
1141         snooze();
1142
1143      /* Snooze after this iteration, unless something happened. */
1144      do_snooze = True;
1145
1146      /* enquire, using poll, whether there is any activity available on
1147         the main socket descriptor.  If so, someone is trying to
1148         connect; get the fd and add it to our table thereof. */
1149      { struct pollfd ufd;
1150        while (1) {
1151           ufd.fd      = main_sd;
1152           ufd.events  = POLLIN;
1153           ufd.revents = 0;
1154           res = poll(&ufd, 1, 0/*ms*/ /* 0=return immediately. */);
1155           if (res == 0) break;
1156
1157           /* ok, we have someone waiting to connect.  Get the sd. */
1158           client_len = sizeof(client_addr);
1159           new_sd = accept(main_sd, (struct sockaddr *)&client_addr,
1160                                                       &client_len);
1161           if (new_sd < 0) {
1162              perror("cannot accept connection ");
1163              panic("main -- accept connection");
1164           }
1165
1166           /* find a place to put it. */
1167	   assert(new_sd > 0);
1168           for (i = 0; i < M_CONNECTIONS; i++)
1169              if (!conn_state[i].in_use)
1170                 break;
1171
1172           if (i >= M_CONNECTIONS) {
1173              fprintf(stderr, "\n\nMore than %d concurrent connections.\n"
1174                      "Restart the server giving --max-connect=INT on the\n"
1175                      "commandline to increase the limit.\n\n",
1176                      M_CONNECTIONS);
1177              exit(1);
1178           }
1179
1180assert(one == 1);
1181int ret = setsockopt( new_sd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
1182assert(ret != -1);
1183
1184           memset(&conn_state[i], 0, sizeof(conn_state[i]));
1185           conn_state[i].in_use     = True;
1186           conn_state[i].conn_sd    = new_sd;
1187           conn_state[i].file_fd    = 0; /* not known yet */
1188           conn_state[i].session_id = next_session_id++;
1189           set_blocking(new_sd);
1190           conn_count++;
1191           do_snooze = False;
1192        } /* while (1) */
1193      }
1194
1195      /* We've processed all new connect requests.  Listen for changes
1196         to the current set of fds.  This requires gathering up all
1197         the known conn_sd values and doing poll() on them. */
1198      static struct pollfd *tmp_pollfd;
1199      if (tmp_pollfd == NULL)
1200         tmp_pollfd = my_malloc(M_CONNECTIONS * sizeof tmp_pollfd[0]);
1201
1202      /* And a parallel array which maps entries in tmp_pollfd back to
1203         entries in conn_state. */
1204      static int *tmp_pollfd_to_conn_state;
1205      if (tmp_pollfd_to_conn_state == NULL)
1206         tmp_pollfd_to_conn_state =
1207            my_malloc(M_CONNECTIONS * sizeof tmp_pollfd_to_conn_state[0]);
1208
1209      j = 0;
1210      for (i = 0; i < M_CONNECTIONS; i++) {
1211         if (!conn_state[i].in_use)
1212            continue;
1213         assert(conn_state[i].conn_sd > 2);
1214         tmp_pollfd[j].fd      = conn_state[i].conn_sd;
1215         tmp_pollfd[j].events  = POLLIN /* | POLLHUP | POLLNVAL */;
1216         tmp_pollfd[j].revents = 0;
1217         tmp_pollfd_to_conn_state[j] = i;
1218         j++;
1219      }
1220
1221      res = poll(tmp_pollfd, j, 20/*ms*/ /* 0=return immediately. */ );
1222      if (res < 0) {
1223         perror("poll(main) failed");
1224         panic("poll(main) failed");
1225      }
1226
1227      /* nothing happened. go round again. */
1228      if (res == 0) {
1229         continue;
1230      }
1231
1232      /* inspect the fds. */
1233      for (i = 0; i < j; i++) {
1234
1235         if (tmp_pollfd[i].revents & POLLIN) {
1236            /* We have some activity on tmp_pollfd[i].  We need to
1237               figure out which conn_state[] entry that corresponds
1238               to, which is what tmp_pollfd_to_conn_state is for. */
1239            Int  conn_no  = tmp_pollfd_to_conn_state[i];
1240            Bool finished = handle_transaction(conn_no);
1241            if (finished) {
1242               /* this connection has been closed or otherwise gone
1243                  bad; forget about it. */
1244               conn_count--;
1245               fflush(stdout);
1246               if (conn_count == 0 && exit_when_zero) {
1247                  if (0) printf("\n");
1248                  fflush(stdout);
1249                  exit_routine();
1250	       }
1251            } else {
1252               // maybe show stats
1253               if (conn_state[i].stats_n_rdok_frames > 0
1254                   && (conn_state[i].stats_n_rdok_frames % 1000) == 0) {
1255                  printf("(%d) SessionID %llu:   sent %llu frames, "
1256                         "%llu MB (unz), %llu MB (z)\n",
1257                         conn_count, conn_state[conn_no].session_id,
1258                         conn_state[conn_no].stats_n_rdok_frames,
1259                         conn_state[conn_no].stats_n_read_unz_bytes / 1000000,
1260                         conn_state[conn_no].stats_n_read_z_bytes / 1000000);
1261                  fflush(stdout);
1262               }
1263            }
1264         }
1265
1266      } /* for (i = 0; i < j; i++) */
1267
1268      do_snooze = False;
1269
1270   } /* while (1) */
1271
1272   /* NOTREACHED */
1273}
1274
1275////////////////////////////////////////////////////
1276#include "../coregrind/m_debuginfo/minilzo-inl.c"
1277
1278/*--------------------------------------------------------------------*/
1279/*--- end                                     valgrind-di-server.c ---*/
1280/*--------------------------------------------------------------------*/
1281