Merge r13421:HEAD from branches/DISRV.  This merges the debuginfo-server
stuff into the trunk.


git-svn-id: svn://svn.valgrind.org/valgrind/trunk@13440 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/auxprogs/valgrind-di-server.c b/auxprogs/valgrind-di-server.c
new file mode 100644
index 0000000..06dfdf8
--- /dev/null
+++ b/auxprogs/valgrind-di-server.c
@@ -0,0 +1,1224 @@
+
+/*--------------------------------------------------------------------*/
+/*--- A simple debuginfo server for Valgrind.                      ---*/
+/*---                                         valgrind-di-server.c ---*/
+/*--------------------------------------------------------------------*/
+
+/* To build for an x86_64-linux host:
+      gcc -g -Wall -O -o valgrind-di-server \
+         auxprogs/valgrind-di-server.c -Icoregrind -Iinclude \
+         -IVEX/pub -DVGO_linux -DVGA_amd64
+
+   To build for an x86 (32-bit) host
+      The same, except change -DVGA_amd64 to -DVGA_x86
+*/
+
+/*
+   This file is part of Valgrind, a dynamic binary instrumentation
+   framework.
+
+   Copyright (C) 2013-2013 Mozilla Foundation
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307, USA.
+
+   The GNU General Public License is contained in the file COPYING.
+*/
+
+/* Contributed by Julian Seward <jseward@acm.org> */
+
+/* This code works (just), but it's a mess.  Cleanups (also for
+   coregrind/m_debuginfo/image.c):
+
+   * Build this file for the host arch, not the target.  But how?
+     Even Tromey had difficulty figuring out how to do that.
+
+   * Change the use of pread w/ fd to FILE*, for the file we're
+     serving.  Or, at least, put a loop around the pread uses
+     so that it works correctly in the case where pread reads more
+     than zero but less than we asked for.
+
+   * CRC3 request/response: pass session-IDs back and forth and
+     check them
+
+   * Check that all error cases result in a FAIL frame being returned.
+
+   * image.c: don't assert in cases where a FAIL frame is returned;
+     instead cause the debuginfo reading to fail gracefully.  (Not
+     sure how to do this)
+
+   * Improve diagnostic printing
+
+   * image.c: do we need to do VG_(write_socket) ?  Will it work
+     just to use ordinary VG_(write) ?
+
+   * Both files: document the reason for setting TCP_NODELAY
+
+   * Add a command line argument saying where the served-from
+     directory is -- changes clo_serverpath.
+
+   * Fix up (common up) massive code duplication between client and
+     server.
+
+   * Tidy up the LZO source files; integrate properly in the build
+     system.
+*/
+
+/*---------------------------------------------------------------*/
+
+/* Include valgrind headers before system headers to avoid problems
+   with the system headers #defining things which are used as names
+   of structure members in vki headers. */
+
+#include "pub_core_basics.h"
+#include "pub_core_libcassert.h"    // For VG_BUGS_TO
+#include "pub_core_vki.h"           // Avoids warnings from 
+                                    // pub_core_libcfile.h
+#include "pub_core_libcfile.h"      // For VG_CLO_DEFAULT_LOGPORT
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <sys/poll.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/stat.h>
+#include <netinet/tcp.h>
+
+#include "../coregrind/m_debuginfo/minilzo.h"
+
+/*---------------------------------------------------------------*/
+
+/* The maximum allowable number concurrent connections. */
+#define M_CONNECTIONS 50
+
+static const char* clo_serverpath = ".";
+
+
+/*---------------------------------------------------------------*/
+
+__attribute__ ((noreturn))
+static void panic ( const char* str )
+{
+   fprintf(stderr,
+           "\nvalgrind-di-server: the "
+           "'impossible' happened:\n   %s\n", str);
+   fprintf(stderr,
+           "Please report this bug at: %s\n\n", VG_BUGS_TO);
+   exit(1);
+}
+
+__attribute__ ((noreturn))
+static void my_assert_fail ( const char* expr, const char* file, int line, const char* fn )
+{
+   fprintf(stderr,
+           "\nvalgrind-di-server: %s:%d (%s): Assertion '%s' failed.\n",
+           file, line, fn, expr );
+   fprintf(stderr,
+           "Please report this bug at: %s\n\n", VG_BUGS_TO);
+   exit(1);
+}
+
+#undef assert
+
+#define assert(expr)                                             \
+  ((void) ((expr) ? 0 :					         \
+	   (my_assert_fail (VG_STRINGIFY(expr),	                 \
+                            __FILE__, __LINE__,                  \
+                            __PRETTY_FUNCTION__), 0)))
+
+
+/*---------------------------------------------------------------*/
+
+/* Holds the state that we need to track, for each connection. */
+typedef
+   struct {
+      // is this entry in use?
+      Bool in_use;
+      // socket descriptor to communicate with client.  Initialised as
+      // soon as this entry is created.
+      int  conn_sd;
+      // fd for the file that we are connected to.  Zero if not
+      // currently connected to any file.
+      int   file_fd;
+      ULong file_size;
+      // Session ID
+      ULong session_id;
+      // How many bytes and chunks sent?
+      ULong stats_n_rdok_frames;
+      ULong stats_n_read_unz_bytes; // bytes via READ (uncompressed)
+      ULong stats_n_read_z_bytes;   // bytes via READ (compressed)
+   }
+   ConnState;
+
+/* The state itself. */
+static int       conn_count = 0;
+static ConnState conn_state[M_CONNECTIONS];
+
+/* Issues unique session ID values. */
+static ULong next_session_id = 1;
+
+
+/*---------------------------------------------------------------*/
+
+// Code that is duplicated with the client :-(
+
+/* The following Adler-32 checksum code is taken from zlib-1.2.3, which
+   has the following copyright notice. */
+/*
+Copyright notice:
+
+ (C) 1995-2004 Jean-loup Gailly and Mark Adler
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  Jean-loup Gailly        Mark Adler
+  jloup@gzip.org          madler@alumni.caltech.edu
+
+If you use the zlib library in a product, we would appreciate *not*
+receiving lengthy legal documents to sign. The sources are provided
+for free but without warranty of any kind.  The library has been
+entirely written by Jean-loup Gailly and Mark Adler; it does not
+include third-party code.
+
+If you redistribute modified sources, we would appreciate that you include
+in the file ChangeLog history information documenting your changes. Please
+read the FAQ for more information on the distribution of modified source
+versions.
+*/
+
+/* Update a running Adler-32 checksum with the bytes buf[0..len-1] and
+   return the updated checksum. If buf is NULL, this function returns
+   the required initial value for the checksum. An Adler-32 checksum is
+   almost as reliable as a CRC32 but can be computed much faster. */
+static
+UInt adler32( UInt adler, const UChar* buf, UInt len )
+{
+#  define BASE 65521UL    /* largest prime smaller than 65536 */
+#  define NMAX 5552
+   /* NMAX is the largest n such that 
+      255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
+
+#  define DO1(buf,i)  {adler += (buf)[i]; sum2 += adler;}
+#  define DO2(buf,i)  DO1(buf,i); DO1(buf,i+1);
+#  define DO4(buf,i)  DO2(buf,i); DO2(buf,i+2);
+#  define DO8(buf,i)  DO4(buf,i); DO4(buf,i+4);
+#  define DO16(buf)   DO8(buf,0); DO8(buf,8);
+
+   /* The zlib sources recommend this definition of MOD if the
+      processor cannot do integer division in hardware. */
+#  define MOD(a) \
+      do { \
+          if (a >= (BASE << 16)) a -= (BASE << 16); \
+          if (a >= (BASE << 15)) a -= (BASE << 15); \
+          if (a >= (BASE << 14)) a -= (BASE << 14); \
+          if (a >= (BASE << 13)) a -= (BASE << 13); \
+          if (a >= (BASE << 12)) a -= (BASE << 12); \
+          if (a >= (BASE << 11)) a -= (BASE << 11); \
+          if (a >= (BASE << 10)) a -= (BASE << 10); \
+          if (a >= (BASE << 9)) a -= (BASE << 9); \
+          if (a >= (BASE << 8)) a -= (BASE << 8); \
+          if (a >= (BASE << 7)) a -= (BASE << 7); \
+          if (a >= (BASE << 6)) a -= (BASE << 6); \
+          if (a >= (BASE << 5)) a -= (BASE << 5); \
+          if (a >= (BASE << 4)) a -= (BASE << 4); \
+          if (a >= (BASE << 3)) a -= (BASE << 3); \
+          if (a >= (BASE << 2)) a -= (BASE << 2); \
+          if (a >= (BASE << 1)) a -= (BASE << 1); \
+          if (a >= BASE) a -= BASE; \
+      } while (0)
+#  define MOD4(a) \
+      do { \
+          if (a >= (BASE << 4)) a -= (BASE << 4); \
+          if (a >= (BASE << 3)) a -= (BASE << 3); \
+          if (a >= (BASE << 2)) a -= (BASE << 2); \
+          if (a >= (BASE << 1)) a -= (BASE << 1); \
+          if (a >= BASE) a -= BASE; \
+      } while (0)
+
+    UInt sum2;
+    UInt n;
+
+    /* split Adler-32 into component sums */
+    sum2 = (adler >> 16) & 0xffff;
+    adler &= 0xffff;
+
+    /* in case user likes doing a byte at a time, keep it fast */
+    if (len == 1) {
+        adler += buf[0];
+        if (adler >= BASE)
+            adler -= BASE;
+        sum2 += adler;
+        if (sum2 >= BASE)
+            sum2 -= BASE;
+        return adler | (sum2 << 16);
+    }
+
+    /* initial Adler-32 value (deferred check for len == 1 speed) */
+    if (buf == NULL)
+        return 1L;
+
+    /* in case short lengths are provided, keep it somewhat fast */
+    if (len < 16) {
+        while (len--) {
+            adler += *buf++;
+            sum2 += adler;
+        }
+        if (adler >= BASE)
+            adler -= BASE;
+        MOD4(sum2);             /* only added so many BASE's */
+        return adler | (sum2 << 16);
+    }
+
+    /* do length NMAX blocks -- requires just one modulo operation */
+    while (len >= NMAX) {
+        len -= NMAX;
+        n = NMAX / 16;          /* NMAX is divisible by 16 */
+        do {
+            DO16(buf);          /* 16 sums unrolled */
+            buf += 16;
+        } while (--n);
+        MOD(adler);
+        MOD(sum2);
+    }
+
+    /* do remaining bytes (less than NMAX, still just one modulo) */
+    if (len) {                  /* avoid modulos if none remaining */
+        while (len >= 16) {
+            len -= 16;
+            DO16(buf);
+            buf += 16;
+        }
+        while (len--) {
+            adler += *buf++;
+            sum2 += adler;
+        }
+        MOD(adler);
+        MOD(sum2);
+    }
+
+    /* return recombined sums */
+    return adler | (sum2 << 16);
+
+#  undef MOD4
+#  undef MOD
+#  undef DO16
+#  undef DO8
+#  undef DO4
+#  undef DO2
+#  undef DO1
+#  undef NMAX
+#  undef BASE
+}
+
+
+/* A frame.  The first 4 bytes of |data| give the kind of the frame,
+   and the rest of it is kind-specific data. */
+typedef  struct { UChar* data; SizeT n_data; }  Frame;
+
+
+static void write_UInt_le ( /*OUT*/UChar* dst, UInt n )
+{
+   Int i;
+   for (i = 0; i <= 3; i++) {
+      dst[i] = (UChar)(n & 0xFF);
+      n >>= 8;
+   }
+}
+
+static UInt read_UInt_le ( UChar* src )
+{
+   UInt r = 0;
+   Int i;
+   for (i = 3; i >= 0; i--) {
+      r <<= 8;
+      r += (UInt)src[i];
+   }
+   return r;
+}
+
+static void write_ULong_le ( /*OUT*/UChar* dst, ULong n )
+{
+   Int i;
+   for (i = 0; i <= 7; i++) {
+      dst[i] = (UChar)(n & 0xFF);
+      n >>= 8;
+   }
+}
+
+static ULong read_ULong_le ( UChar* src )
+{
+   ULong r = 0;
+   Int i;
+   for (i = 7; i >= 0; i--) {
+      r <<= 8;
+      r += (ULong)src[i];
+   }
+   return r;
+}
+
+static Frame* mk_Frame_asciiz ( const char* tag, const char* str )
+{
+   assert(strlen(tag) == 4);
+   Frame* f = calloc(sizeof(Frame), 1);
+   size_t n_str = strlen(str);
+   f->n_data = 4 + n_str + 1;
+   f->data = calloc(f->n_data, 1);
+   memcpy(&f->data[0], tag, 4);
+   memcpy(&f->data[4], str, n_str);
+   assert(f->data[4 + n_str] == 0);
+   return f;
+}
+
+static Bool parse_Frame_noargs ( Frame* fr, const HChar* tag )
+{
+   assert(strlen(tag) == 4);
+   if (!fr || !fr->data) return False;
+   if (fr->n_data < 4) return False;
+   if (memcmp(&fr->data[0], tag, 4) != 0) return False;
+   if (fr->n_data != 4) return False;
+   return True;
+}
+
+static Bool parse_Frame_asciiz ( Frame* fr, const HChar* tag,
+                                 /*OUT*/UChar** str )
+{
+   assert(strlen(tag) == 4);
+   if (!fr || !fr->data) return False;
+   if (fr->n_data < 4) return False;
+   if (memcmp(&fr->data[0], tag, 4) != 0) return False;
+   if (fr->n_data < 5) return False; // else there isn't even enough
+                                     // space for the terminating zero
+   /* Find the terminating zero and ensure it's right at the end
+      of the data.  If not, the frame is malformed. */
+   SizeT i = 4;
+   while (True) {
+      if (i >= fr->n_data) break;
+      if (fr->data[i] == 0) break;
+      i++;
+   }
+   assert(i <= fr->n_data);
+   if (i == fr->n_data-1 && fr->data[i] == 0) {
+      *str = &fr->data[4];
+      return True;
+   } else {
+      return False;
+   }
+}
+
+static Frame* mk_Frame_le64 ( const HChar* tag, ULong n1 )
+{
+   assert(strlen(tag) == 4);
+   Frame* f = calloc(sizeof(Frame), 1);
+   f->n_data = 4 + 1*8;
+   f->data = calloc(f->n_data, 1);
+   memcpy(&f->data[0], tag, 4);
+   write_ULong_le(&f->data[4 + 0*8], n1);
+   return f;
+}
+
+static Frame* mk_Frame_le64_le64 ( const HChar* tag, ULong n1, ULong n2 )
+{
+   assert(strlen(tag) == 4);
+   Frame* f = calloc(sizeof(Frame), 1);
+   f->n_data = 4 + 2*8;
+   f->data = calloc(f->n_data, 1);
+   memcpy(&f->data[0], tag, 4);
+   write_ULong_le(&f->data[4 + 0*8], n1);
+   write_ULong_le(&f->data[4 + 1*8], n2);
+   return f;
+}
+
+static Bool parse_Frame_le64_le64_le64 ( Frame* fr, const HChar* tag,
+                                         /*OUT*/ULong* n1, /*OUT*/ULong* n2,
+                                         /*OUT*/ULong* n3 )
+{
+   assert(strlen(tag) == 4);
+   if (!fr || !fr->data) return False;
+   if (fr->n_data < 4) return False;
+   if (memcmp(&fr->data[0], tag, 4) != 0) return False;
+   if (fr->n_data != 4 + 3*8) return False;
+   *n1 = read_ULong_le(&fr->data[4 + 0*8]);
+   *n2 = read_ULong_le(&fr->data[4 + 1*8]);
+   *n3 = read_ULong_le(&fr->data[4 + 2*8]);
+   return True;
+}
+
+static Frame* mk_Frame_le64_le64_le64_bytes ( 
+                 const HChar* tag,
+                 ULong n1, ULong n2, ULong n3, ULong n_data,
+                 /*OUT*/UChar** data )
+{
+   assert(strlen(tag) == 4);
+   Frame* f = calloc(sizeof(Frame), 1);
+   f->n_data = 4 + 3*8 + n_data;
+   f->data = calloc(f->n_data, 1);
+   memcpy(&f->data[0], tag, 4);
+   write_ULong_le(&f->data[4 + 0*8], n1);
+   write_ULong_le(&f->data[4 + 1*8], n2);
+   write_ULong_le(&f->data[4 + 2*8], n3);
+   *data = &f->data[4 + 3*8];
+   return f;
+}
+
+static void free_Frame ( Frame* fr )
+{
+   assert(fr && fr->data);
+   free(fr->data);
+   free(fr);
+}
+
+
+static void set_blocking ( int sd )
+{
+   int res;
+   res = fcntl(sd, F_GETFL);
+   res = fcntl(sd, F_SETFL, res & ~O_NONBLOCK);
+   if (res != 0) {
+      perror("fcntl failed");
+      panic("set_blocking");
+   }
+}
+
+
+#if 0
+static void set_nonblocking ( int sd )
+{
+   int res;
+   res = fcntl(sd, F_GETFL);
+   res = fcntl(sd, F_SETFL, res | O_NONBLOCK);
+   if (res != 0) {
+      perror("fcntl failed");
+      panic("set_nonblocking");
+   }
+}
+#endif
+
+
+/* Tries to read 'len' bytes from fd, blocking if necessary.  Assumes
+   fd has been set in blocking mode.  If it returns with the number of
+   bytes read < len, it means that either fd was closed, or there was
+   an error on it. */
+static SizeT my_read ( Int fd, UChar* buf, SizeT len )
+{
+  //set_blocking(fd);
+   SizeT nRead = 0;
+   while (1) {
+      if (nRead == len) return nRead;
+      assert(nRead < len);
+      SizeT nNeeded = len - nRead;
+      assert(nNeeded > 0);
+      SSizeT n = read(fd, &buf[nRead], nNeeded);
+      if (n <= 0) return nRead; /* error or EOF */
+      nRead += n;
+   }
+}
+
+/* Tries to write 'len' bytes to fd, blocking if necessary.  Assumes
+   fd has been set in blocking mode.  If it returns with the number of
+   bytes written < len, it means that either fd was closed, or there was
+   an error on it. */
+static SizeT my_write ( Int fd, UChar* buf, SizeT len )
+{
+  //set_nonblocking(fd);
+   SizeT nWritten = 0;
+   while (1) {
+      if (nWritten == len) return nWritten;
+      assert(nWritten < len);
+      SizeT nStillToDo = len - nWritten;
+      assert(nStillToDo > 0);
+      SSizeT n = write(fd, &buf[nWritten], nStillToDo);
+      if (n < 0) return nWritten; /* error or EOF */
+      nWritten += n;
+   }
+}
+
+
+static UInt calc_gnu_debuglink_crc32(/*OUT*/Bool* ok, int fd, ULong size)
+{
+  static const UInt crc32_table[256] =
+    {
+      0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419,
+      0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4,
+      0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07,
+      0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
+      0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856,
+      0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+      0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4,
+      0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+      0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3,
+      0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a,
+      0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599,
+      0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+      0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190,
+      0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f,
+      0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e,
+      0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+      0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed,
+      0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+      0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3,
+      0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
+      0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a,
+      0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5,
+      0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010,
+      0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+      0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17,
+      0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6,
+      0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615,
+      0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
+      0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344,
+      0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+      0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a,
+      0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+      0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1,
+      0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c,
+      0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef,
+      0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+      0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe,
+      0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31,
+      0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c,
+      0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+      0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b,
+      0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+      0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1,
+      0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
+      0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278,
+      0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7,
+      0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66,
+      0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+      0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605,
+      0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8,
+      0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b,
+      0x2d02ef8d
+    };
+
+      /* Work through the image in 1 KB chunks. */
+      UInt  crc      = 0xFFFFFFFF;
+      ULong img_szB  = size;
+      ULong curr_off = 0;
+      while (1) {
+         assert(curr_off >= 0 && curr_off <= img_szB);
+         if (curr_off == img_szB) break;
+         ULong avail = img_szB - curr_off;
+         assert(avail > 0 && avail <= img_szB);
+         if (avail > 65536) avail = 65536;
+         UChar buf[65536];
+         Int nRead = pread(fd, buf, avail, curr_off);
+         if (nRead <= 0) { /* EOF or error on the file; neither should happen */
+            *ok = False;
+            return 0;
+         }
+         /* this is a kludge .. we should loop around pread and deal
+            with short reads, for whatever reason */
+         assert(nRead == avail);
+         UInt i;
+         for (i = 0; i < (UInt)nRead; i++)
+            crc = crc32_table[(crc ^ buf[i]) & 0xff] ^ (crc >> 8);
+         curr_off += nRead;
+      }
+      *ok = True;
+      return ~crc & 0xFFFFFFFF;
+   }
+
+
+/*---------------------------------------------------------------*/
+
+/* Handle a transaction for conn_state[conn_no].  There is incoming
+   data available; read it and send back an appropriate response.
+   Returns a boolean indicating whether the connection has been
+   closed; in which case this function does all necessary cleanup and
+   leaves conn_state[conn_no] in a not-in-use state. */
+
+static Bool handle_transaction ( int conn_no )
+{
+   Frame* req = NULL; /* the request frame that we receive */
+   Frame* res = NULL; /* the response frame that we send back */
+
+   assert(conn_no >= 0 && conn_no < M_CONNECTIONS);
+   assert(conn_state[conn_no].in_use);
+
+   //printf("SERVER: handle_transaction(%d)\n", conn_no); fflush(stdout);
+
+   Int sd = conn_state[conn_no].conn_sd;
+
+   /* Get a frame out of the channel. */
+   UChar rd_first8[8];  // adler32; length32
+   { Int r = my_read(sd, &rd_first8[0], 8);
+     if (r == 0) goto client_closed_conn;
+     if (r != 8) goto fail;
+   }
+   UInt rd_adler = read_UInt_le(&rd_first8[0]);
+   UInt rd_len   = read_UInt_le(&rd_first8[4]);
+   /* Allocate a Frame to hold the result data, and read into it. */
+   // Reject obviously-insane length fields.
+   if (rd_len > 4*1024*1024) goto fail;
+   assert(req == NULL);
+   req = calloc(sizeof(Frame), 1);
+   req->n_data = rd_len;
+   req->data = calloc(rd_len, 1);
+   if (rd_len > 0) {
+      Int r = my_read(sd, req->data, req->n_data);
+      if (r != rd_len) goto fail;
+   }
+//printf("SERVER: recv %c%c%c%c\n", req->data[0], req->data[1], req->data[2], req->data[3]); fflush(stdout);
+
+   /* Compute the checksum for the received data, and check it. */
+   UInt adler = adler32(0, NULL, 0); // initial value
+   adler = adler32(adler, &rd_first8[4], 4);
+   if (req->n_data > 0) 
+      adler = adler32(adler, req->data, req->n_data);
+
+   if (adler/*computed*/ != rd_adler/*expected*/) goto fail;
+
+   /* Now we have a request frame.  Cook up a response. */
+   assert(res == NULL);
+
+   UChar* filename = NULL;
+   ULong req_session_id = 0, req_offset = 0, req_len = 0;
+
+   if (parse_Frame_noargs(req, "VERS")) {
+      res = mk_Frame_asciiz("VEOK", "Valgrind Debuginfo Server, Version 1");
+   }
+   else
+   if (parse_Frame_noargs(req, "CRC3")) {
+      /* FIXME: add a session ID to this request, and check it */
+      if (conn_state[conn_no].file_fd == 0) {
+         res = mk_Frame_asciiz("CRC3", "FAIL: not connected to file");
+      } else {
+         Bool ok    = False;
+         UInt crc32 = calc_gnu_debuglink_crc32(&ok, 
+                                               conn_state[conn_no].file_fd,
+                                               conn_state[conn_no].file_size);
+         if (ok) {
+            res = mk_Frame_le64("CROK", (ULong)crc32);
+         } else {
+            res = mk_Frame_asciiz("FAIL", "CRC3: I/O error reading file");
+         }
+      }
+   }
+   else
+   if (parse_Frame_asciiz(req, "OPEN", &filename)) {
+      Bool ok = True;
+      int  fd = 0;
+      if (conn_state[conn_no].file_fd != 0) {
+         res = mk_Frame_asciiz("FAIL", "OPEN: already connected to file");
+         ok = False;
+      }
+      if (ok) {
+         assert(clo_serverpath);
+         fd = open((char*)filename, O_RDONLY);
+         if (fd == -1) {
+            res = mk_Frame_asciiz("FAIL", "OPEN: cannot open file");
+            ok = False;
+         } else {
+            assert(fd > 2);
+         }
+      }
+      if (ok) {
+         struct stat stat_buf;
+         int r = fstat(fd, &stat_buf);
+         if (r != 0) {
+            res = mk_Frame_asciiz("FAIL", "OPEN: cannot stat file");
+            ok = False;
+         }
+         if (ok && stat_buf.st_size == 0) {
+            res = mk_Frame_asciiz("FAIL", "OPEN: file has zero size");
+            ok = False;
+         }
+         if (ok) {
+            conn_state[conn_no].file_fd   = fd;
+            conn_state[conn_no].file_size = stat_buf.st_size;
+            assert(res == NULL);
+            res = mk_Frame_le64_le64("OPOK", conn_state[conn_no].session_id,
+                                             conn_state[conn_no].file_size);
+            printf("(%d) SessionID %llu: open successful for \"%s\"\n",
+                   conn_count, conn_state[conn_no].session_id, filename );
+            fflush(stdout);
+         }
+      }
+   }
+   else
+   if (parse_Frame_le64_le64_le64(req, "READ", &req_session_id,
+                                  &req_offset, &req_len)) {
+      /* Because each new connection is associated with its own socket
+         descriptor and hence with a particular conn_no, the requested
+         session-ID is redundant -- it must be the one associated with
+         this slot.  But check anyway. */
+      Bool ok = True;
+      if (req_session_id != conn_state[conn_no].session_id) {
+         req = mk_Frame_asciiz("FAIL", "READ: invalid session ID");
+         ok = False;
+      }
+      /* Check we're connected to a file, and if so range-check the
+         request. */
+      if (ok && conn_state[conn_no].file_fd == 0) {
+         res = mk_Frame_asciiz("FAIL", "READ: no associated file");
+         ok = False;
+      }
+      if (ok && (req_len == 0 || req_len > 4*1024*1024)) {
+         res = mk_Frame_asciiz("FAIL", "READ: invalid request size");
+         ok = False;
+      }
+      if (ok && req_len + req_offset > conn_state[conn_no].file_size) {
+         res = mk_Frame_asciiz("FAIL", "READ: request exceeds file size");
+         ok = False;
+      }
+      /* Try to read the file. */
+      if (ok) {
+         /* First, allocate a temp buf and read from the file into it. */
+         /* FIXME: what if pread reads short and we have to redo it? */
+         UChar* unzBuf = malloc(req_len);
+         size_t nRead = pread(conn_state[conn_no].file_fd,
+                              unzBuf, req_len, req_offset);
+         if (nRead != req_len) {
+            free_Frame(res);
+            res = mk_Frame_asciiz("FAIL", "READ: I/O error reading file");
+            ok = False;
+         }         UInt zLen = 0;
+         if (ok) {
+            // Now compress it with LZO.  LZO appears to recommend
+            // the worst-case output size as (in_len + in_len / 16 + 67).
+            // Be more conservative here.
+#           define STACK_ALLOC(var,size) \
+               lzo_align_t __LZO_MMODEL \
+                  var [ ((size) \
+                        + (sizeof(lzo_align_t) - 1)) / sizeof(lzo_align_t) ]
+            STACK_ALLOC(wrkmem, LZO1X_1_MEM_COMPRESS);
+#           undef STACK_ALLOC
+            UInt zLenMax = req_len + req_len / 4 + 1024;
+            UChar* zBuf = malloc(zLenMax);
+            zLen = zLenMax;
+            Int lzo_rc = lzo1x_1_compress(unzBuf, req_len,
+                                          zBuf, (lzo_uint*)&zLen, wrkmem); 
+            if (lzo_rc == LZO_E_OK) {
+              //printf("XXXXX req_len %u  zLen %u\n", (UInt)req_len, (UInt)zLen);
+               assert(zLen <= zLenMax);
+               /* Make a frame to put the results in.  Bytes 24 and
+                  onwards need to be filled from the compressed data,
+                  and 'buf' is set to point to the right bit. */
+               UChar* buf = NULL;
+               res = mk_Frame_le64_le64_le64_bytes
+                 ("RDOK", req_session_id, req_offset, req_len, zLen, &buf);
+               assert(res);
+               assert(buf);
+               memcpy(buf, zBuf, zLen);
+               // Update stats
+               conn_state[conn_no].stats_n_rdok_frames++;
+               conn_state[conn_no].stats_n_read_unz_bytes += req_len;
+               conn_state[conn_no].stats_n_read_z_bytes   += zLen;
+            } else {
+               ok = False;
+               free_Frame(res);
+               res = mk_Frame_asciiz("FAIL", "READ: LZO failed");
+            }
+            free(zBuf);
+         }
+         free(unzBuf);
+      }
+   }
+   else {
+      res = mk_Frame_asciiz("FAIL", "Invalid request frame type");
+   }
+
+   /* All paths through the above should result in an assignment to |res|. */
+   assert(res != NULL);
+
+   /* And send the response frame back to the client. */
+   /* What goes on the wire is:
+         adler(le32) n_data(le32) data[0 .. n_data-1]
+      where the checksum covers n_data as well as data[].
+   */
+   /* The initial Adler-32 value */
+   adler = adler32(0, NULL, 0);
+
+   /* Fold in the length field, encoded as le32. */
+   UChar wr_first8[8];
+   write_UInt_le(&wr_first8[4], res->n_data);
+   adler = adler32(adler, &wr_first8[4], 4);
+   /* Fold in the data values */
+   adler = adler32(adler, res->data, res->n_data);
+   write_UInt_le(&wr_first8[0], adler);
+
+   Int r = my_write(sd, &wr_first8[0], 8);
+   if (r != 8) goto fail;
+   assert(res->n_data >= 4); // else ill formed -- no KIND field
+   r = my_write(sd, res->data, res->n_data);
+   if (r != res->n_data) goto fail;
+
+//printf("SERVER: send %c%c%c%c\n", res->data[0], res->data[1], res->data[2], res->data[3]); fflush(stdout);
+
+   /* So, success. */
+   if (req) free_Frame(req);
+   if (res) free_Frame(res);
+   return False;  /* "connection still in use" */
+
+   // Is there any difference between these?
+  client_closed_conn:
+  fail:
+   if (conn_state[conn_no].conn_sd > 0)
+      close(conn_state[conn_no].conn_sd);
+   if (conn_state[conn_no].file_fd > 0)
+      close(conn_state[conn_no].file_fd);
+
+   if (conn_state[conn_no].stats_n_rdok_frames > 0) {
+      printf("(%d) SessionID %llu:   sent %llu frames, "
+             "%llu MB (unz), %llu MB (z), ratio %4.2f:1\n",
+             conn_count, conn_state[conn_no].session_id,
+             conn_state[conn_no].stats_n_rdok_frames,
+             conn_state[conn_no].stats_n_read_unz_bytes / 1000000,
+             conn_state[conn_no].stats_n_read_z_bytes / 1000000,
+             (double)conn_state[conn_no].stats_n_read_unz_bytes
+               / (double)conn_state[conn_no].stats_n_read_z_bytes);
+      printf("(%d) SessionID %llu: closed\n",
+             conn_count, conn_state[conn_no].session_id);
+
+      fflush(stdout);
+   }
+
+   memset(&conn_state[conn_no], 0, sizeof(conn_state[conn_no]));
+   if (req) free_Frame(req);
+   if (res) free_Frame(res);
+   return True; /* "connection has been closed" */
+}
+
+
+/*---------------------------------------------------------------*/
+
+
+
+#if 0
+static void copyout ( char* buf, int nbuf )
+{
+   int i;
+   for (i = 0; i < nbuf; i++) {
+      if (buf[i] == '\n') {
+         fprintf(stdout, "\n(%d) ", conn_count);
+      } else {
+         __attribute__((unused)) size_t ignored 
+            = fwrite(&buf[i], 1, 1, stdout);
+      }
+   }
+   fflush(stdout);
+}
+
+static int read_from_sd ( int sd )
+{
+   char buf[100];
+   int n;
+
+   set_blocking(sd);
+   n = read(sd, buf, 99);
+   if (n <= 0) return 0; /* closed */
+   copyout(buf, n);
+
+   set_nonblocking(sd);
+   while (1) {
+      n = read(sd, buf, 100);
+      if (n <= 0) return 1; /* not closed */
+      copyout(buf, n);
+   }
+}
+#endif
+
+static void snooze ( void )
+{
+   struct timespec req;
+   req.tv_sec = 0;
+   req.tv_nsec = 200 * 1000 * 1000;
+   nanosleep(&req,NULL);
+}
+
+
+/* returns 0 if invalid, else port # */
+static int atoi_portno ( const char* str )
+{
+   int n = 0;
+   while (1) {
+      if (*str == 0) 
+         break;
+      if (*str < '0' || *str > '9')
+         return 0;
+      n = 10*n + (int)(*str - '0');
+      str++;
+      if (n >= 65536) 
+         return 0;
+   }
+   if (n < 1024)
+      return 0;
+   return n;
+}
+
+
+static void usage ( void )
+{
+   fprintf(stderr, 
+      "\n"
+      "usage is:\n"
+      "\n"
+      "   valgrind-di-server [--exit-at-zero|-e] [port-number]\n"
+      "\n"
+      "   where   --exit-at-zero or -e causes the listener to exit\n"
+      "           when the number of connections falls back to zero\n"
+      "           (the default is to keep listening forever)\n"
+      "\n"
+      "           port-number is the default port on which to listen for\n"
+      "           connections.  It must be between 1024 and 65535.\n"
+      "           Current default is %d.\n"
+      "\n"
+      ,
+      VG_CLO_DEFAULT_LOGPORT
+   );
+   exit(1);
+}
+
+
+static void banner ( const char* str )
+{
+   time_t t;
+   t = time(NULL);
+   printf("valgrind-di-server %s at %s", str, ctime(&t));
+   fflush(stdout);
+}
+
+
+static void exit_routine ( void )
+{
+   banner("exited");
+   exit(0);
+}
+
+
+static void sigint_handler ( int signo )
+{
+   exit_routine();
+}
+
+
+int main (int argc, char** argv) 
+{
+   int    i, j, res, one;
+   int    main_sd, new_sd;
+   socklen_t client_len;
+   struct sockaddr_in client_addr, server_addr;
+
+   char /*bool*/ exit_when_zero = 0;
+   int           port = VG_CLO_DEFAULT_LOGPORT;
+
+   for (i = 1; i < argc; i++) {
+      if (0==strcmp(argv[i], "--exit-at-zero")
+          || 0==strcmp(argv[i], "-e")) {
+         exit_when_zero = 1;
+      }
+      else
+      if (atoi_portno(argv[i]) > 0) {
+         port = atoi_portno(argv[i]);
+      }
+      else
+      usage();
+   }
+
+   banner("started");
+   signal(SIGINT, sigint_handler);
+
+   conn_count = 0;
+   memset(&conn_state, 0, sizeof(conn_state));
+
+   /* create socket */
+   main_sd = socket(AF_INET, SOCK_STREAM, 0);
+   if (main_sd < 0) {
+      perror("cannot open socket ");
+      panic("main -- create socket");
+   }
+
+   /* allow address reuse to avoid "address already in use" errors */
+
+   one = 1;
+   if (setsockopt(main_sd, SOL_SOCKET, SO_REUSEADDR, 
+		  &one, sizeof(one)) < 0) {
+      perror("cannot enable address reuse ");
+      panic("main -- enable address reuse");
+   }
+
+   /* bind server port */
+   server_addr.sin_family      = AF_INET;
+   server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+   server_addr.sin_port        = htons(port);
+  
+   if (bind(main_sd, (struct sockaddr *) &server_addr, 
+                     sizeof(server_addr) ) < 0) {
+      perror("cannot bind port ");
+      panic("main -- bind port");
+   }
+
+   res = listen(main_sd, M_CONNECTIONS);
+   if (res != 0) {
+      perror("listen failed ");
+      panic("main -- listen");
+   }
+
+   Bool do_snooze = False;  
+   while (1) {
+
+      if (0 && do_snooze)
+         snooze();
+
+      /* Snooze after this iteration, unless something happened. */
+      do_snooze = True;
+
+      /* enquire, using poll, whether there is any activity available on
+         the main socket descriptor.  If so, someone is trying to
+         connect; get the fd and add it to our table thereof. */
+      { struct pollfd ufd;
+        while (1) {
+           ufd.fd      = main_sd;
+           ufd.events  = POLLIN;
+           ufd.revents = 0;
+           res = poll(&ufd, 1, 0/*ms*/ /* 0=return immediately. */);
+           if (res == 0) break;
+
+           /* ok, we have someone waiting to connect.  Get the sd. */
+           client_len = sizeof(client_addr);
+           new_sd = accept(main_sd, (struct sockaddr *)&client_addr, 
+                                                       &client_len);
+           if (new_sd < 0) {
+              perror("cannot accept connection ");
+              panic("main -- accept connection");
+           }
+
+           /* find a place to put it. */
+	   assert(new_sd > 0);
+           for (i = 0; i < M_CONNECTIONS; i++)
+              if (!conn_state[i].in_use)
+                 break;
+
+           if (i >= M_CONNECTIONS) {
+              fprintf(stderr, "Too many concurrent connections.  "
+                              "Increase M_CONNECTIONS and recompile.\n");
+              panic("main -- too many concurrent connections");
+           }
+
+assert(one == 1);
+int ret = setsockopt( new_sd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
+assert(ret != -1);
+
+           memset(&conn_state[i], 0, sizeof(conn_state[i]));
+           conn_state[i].in_use     = True;
+           conn_state[i].conn_sd    = new_sd;
+           conn_state[i].file_fd    = 0; /* not known yet */
+           conn_state[i].session_id = next_session_id++;
+           set_blocking(new_sd);
+           conn_count++;
+           do_snooze = False;
+        } /* while (1) */
+      }
+
+      /* We've processed all new connect requests.  Listen for changes
+         to the current set of fds.  This requires gathering up all
+         the known conn_sd values and doing poll() on them. */
+      struct pollfd tmp_pollfd[M_CONNECTIONS];
+      /* And a parallel array which maps entries in tmp_pollfd back to
+         entries in conn_state. */
+      int tmp_pollfd_to_conn_state[M_CONNECTIONS];
+      j = 0;
+      for (i = 0; i < M_CONNECTIONS; i++) {
+         if (!conn_state[i].in_use)
+            continue;
+         assert(conn_state[i].conn_sd > 2);
+         tmp_pollfd[j].fd      = conn_state[i].conn_sd;
+         tmp_pollfd[j].events  = POLLIN /* | POLLHUP | POLLNVAL */;
+         tmp_pollfd[j].revents = 0;
+         tmp_pollfd_to_conn_state[j] = i;
+         j++;
+      }
+
+      res = poll(tmp_pollfd, j, 20/*ms*/ /* 0=return immediately. */ );
+      if (res < 0) {
+         perror("poll(main) failed");
+         panic("poll(main) failed");
+      }
+    
+      /* nothing happened. go round again. */
+      if (res == 0) {
+         continue;
+      }
+
+      /* inspect the fds. */
+      for (i = 0; i < j; i++) {
+ 
+         if (tmp_pollfd[i].revents & POLLIN) {
+            /* We have some activity on tmp_pollfd[i].  We need to
+               figure out which conn_state[] entry that corresponds
+               to, which is what tmp_pollfd_to_conn_state is for. */
+            Int  conn_no  = tmp_pollfd_to_conn_state[i];
+            Bool finished = handle_transaction(conn_no);
+            if (finished) {
+               /* this connection has been closed or otherwise gone
+                  bad; forget about it. */
+               conn_count--;
+               fflush(stdout);
+               if (conn_count == 0 && exit_when_zero) {
+                  if (0) printf("\n");
+                  fflush(stdout);
+                  exit_routine();
+	       }
+            } else {
+               // maybe show stats
+               if (conn_state[i].stats_n_rdok_frames > 0
+                   && (conn_state[i].stats_n_rdok_frames % 1000) == 0) {
+                  printf("(%d) SessionID %llu:   sent %llu frames, "
+                         "%llu MB (unz), %llu MB (z)\n",
+                         conn_count, conn_state[conn_no].session_id,
+                         conn_state[conn_no].stats_n_rdok_frames,
+                         conn_state[conn_no].stats_n_read_unz_bytes / 1000000,
+                         conn_state[conn_no].stats_n_read_z_bytes / 1000000);
+                  fflush(stdout);
+               }
+            }
+         }
+
+      } /* for (i = 0; i < j; i++) */
+  
+      do_snooze = False;
+
+   } /* while (1) */
+
+   /* NOTREACHED */
+}
+
+////////////////////////////////////////////////////
+#include "../coregrind/m_debuginfo/minilzo-inl.c"
+
+/*--------------------------------------------------------------------*/
+/*--- end                                     valgrind-di-server.c ---*/
+/*--------------------------------------------------------------------*/