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