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 ---*/
+/*--------------------------------------------------------------------*/