Blame src/util/uthash/tests/hashscan.c

Packit Service 9e77c8
/*
Packit Service 9e77c8
Copyright (c) 2005-2013, Troy D. Hanson    http://troydhanson.github.com/uthash/
Packit Service 9e77c8
All rights reserved.
Packit Service 9e77c8
Packit Service 9e77c8
Redistribution and use in source and binary forms, with or without
Packit Service 9e77c8
modification, are permitted provided that the following conditions are met:
Packit Service 9e77c8
Packit Service 9e77c8
    * Redistributions of source code must retain the above copyright
Packit Service 9e77c8
      notice, this list of conditions and the following disclaimer.
Packit Service 9e77c8
Packit Service 9e77c8
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
Packit Service 9e77c8
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
Packit Service 9e77c8
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
Packit Service 9e77c8
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
Packit Service 9e77c8
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
Packit Service 9e77c8
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
Packit Service 9e77c8
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
Packit Service 9e77c8
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
Packit Service 9e77c8
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
Packit Service 9e77c8
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
Packit Service 9e77c8
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Packit Service 9e77c8
*/
Packit Service 9e77c8
Packit Service 9e77c8
#include <string.h>
Packit Service 9e77c8
#include <errno.h>
Packit Service 9e77c8
#include <stdlib.h>
Packit Service 9e77c8
#include <stdio.h>
Packit Service 9e77c8
#include <sys/stat.h>
Packit Service 9e77c8
#include <fcntl.h>
Packit Service 9e77c8
#include <inttypes.h>
Packit Service 9e77c8
#include <sys/ptrace.h>
Packit Service 9e77c8
#include <sys/types.h>
Packit Service 9e77c8
#include <unistd.h>
Packit Service 9e77c8
#include <sys/wait.h>
Packit Service 9e77c8
#include <assert.h>
Packit Service 9e77c8
Packit Service 9e77c8
#ifdef __FreeBSD__
Packit Service 9e77c8
#include <sys/param.h>  /* MAXPATHLEN */
Packit Service 9e77c8
#include <vm/vm.h>      /* VM_PROT_* flags */
Packit Service 9e77c8
#endif
Packit Service 9e77c8
Packit Service 9e77c8
/* need this defined so offsetof can give us bloom offsets in UT_hash_table */
Packit Service 9e77c8
#define HASH_BLOOM 16
Packit Service 9e77c8
#include "uthash.h"
Packit Service 9e77c8
Packit Service 9e77c8
#ifdef __FreeBSD__
Packit Service 9e77c8
typedef struct {
Packit Service 9e77c8
  void *start;
Packit Service 9e77c8
  void *end;
Packit Service 9e77c8
} vma_t;
Packit Service 9e77c8
#else
Packit Service 9e77c8
typedef struct {
Packit Service 9e77c8
  off_t start;
Packit Service 9e77c8
  off_t end;
Packit Service 9e77c8
  char perms[4];   /* rwxp */
Packit Service 9e77c8
  char device[5];  /* fd:01 or 00:00 */
Packit Service 9e77c8
} vma_t;
Packit Service 9e77c8
#endif
Packit Service 9e77c8
Packit Service 9e77c8
const uint32_t sig = HASH_SIGNATURE;
Packit Service 9e77c8
int verbose=0;
Packit Service 9e77c8
int getkeys=0;
Packit Service 9e77c8
Packit Service 9e77c8
#define vv(...)  do {if (verbose>0) printf(__VA_ARGS__);} while(0)
Packit Service 9e77c8
#define vvv(...) do {if (verbose>1) printf(__VA_ARGS__);} while(0)
Packit Service 9e77c8
Packit Service 9e77c8
/* these id's are arbitrary, only meaningful within this file */
Packit Service 9e77c8
#define JEN 1
Packit Service 9e77c8
#define BER 2
Packit Service 9e77c8
#define SFH 3
Packit Service 9e77c8
#define SAX 4
Packit Service 9e77c8
#define FNV 5
Packit Service 9e77c8
#define OAT 6
Packit Service 9e77c8
#define MUR 7
Packit Service 9e77c8
#define NUM_HASH_FUNCS 8 /* includes id 0, the non-function */
Packit Service 9e77c8
char *hash_fcns[] = {"???","JEN","BER","SFH","SAX","FNV","OAT","MUR"};
Packit Service 9e77c8
Packit Service 9e77c8
/* given a peer key/len/hashv, reverse engineer its hash function */
Packit Service 9e77c8
int infer_hash_function(char *key, size_t keylen, uint32_t hashv) {
Packit Service 9e77c8
  uint32_t obkt, ohashv, num_bkts=0x01000000; /* anything ok */
Packit Service 9e77c8
  /* BER SAX FNV OAT JEN SFH */
Packit Service 9e77c8
  HASH_JEN(key,keylen,num_bkts,ohashv,obkt); if (ohashv == hashv) return JEN;
Packit Service 9e77c8
  HASH_BER(key,keylen,num_bkts,ohashv,obkt); if (ohashv == hashv) return BER;
Packit Service 9e77c8
  HASH_SFH(key,keylen,num_bkts,ohashv,obkt); if (ohashv == hashv) return SFH;
Packit Service 9e77c8
  HASH_SAX(key,keylen,num_bkts,ohashv,obkt); if (ohashv == hashv) return SAX;
Packit Service 9e77c8
  HASH_FNV(key,keylen,num_bkts,ohashv,obkt); if (ohashv == hashv) return FNV;
Packit Service 9e77c8
  HASH_OAT(key,keylen,num_bkts,ohashv,obkt); if (ohashv == hashv) return OAT;
Packit Service 9e77c8
  HASH_MUR(key,keylen,num_bkts,ohashv,obkt); if (ohashv == hashv) return MUR;
Packit Service 9e77c8
  obkt++; // this quiets an unused variable warning. yes, this is a ugly hack
Packit Service 9e77c8
  return 0;
Packit Service 9e77c8
}
Packit Service 9e77c8
Packit Service 9e77c8
/* read peer's memory from addr for len bytes, store into our dst */
Packit Service 9e77c8
#ifdef __FreeBSD__
Packit Service 9e77c8
int read_mem(void *dst, pid_t pid, void *start, size_t len) {
Packit Service 9e77c8
  struct ptrace_io_desc io_desc;
Packit Service 9e77c8
  int ret;
Packit Service 9e77c8
Packit Service 9e77c8
  io_desc.piod_op = PIOD_READ_D;
Packit Service 9e77c8
  io_desc.piod_offs = start;
Packit Service 9e77c8
  io_desc.piod_addr = dst;
Packit Service 9e77c8
  io_desc.piod_len = len;
Packit Service 9e77c8
Packit Service 9e77c8
  ret = ptrace(PT_IO, pid, (void *) &io_desc, 0);
Packit Service 9e77c8
  
Packit Service 9e77c8
  if (ret) {
Packit Service 9e77c8
    vv("read_mem: ptrace failed: %s\n", strerror(errno));
Packit Service 9e77c8
    return -1;
Packit Service 9e77c8
  } else if (io_desc.piod_len != len) {
Packit Service 9e77c8
    vv("read_mem: short read!\n");
Packit Service 9e77c8
    return -1;
Packit Service 9e77c8
  }
Packit Service 9e77c8
Packit Service 9e77c8
  return 0;
Packit Service 9e77c8
}
Packit Service 9e77c8
#else
Packit Service 9e77c8
int read_mem(void *dst, int fd, off_t start, size_t len) {
Packit Service 9e77c8
  int rc;
Packit Service 9e77c8
  size_t bytes_read=0;
Packit Service 9e77c8
  if (lseek(fd, start, SEEK_SET) == (off_t)-1) {
Packit Service 9e77c8
    fprintf(stderr, "lseek failed: %s\n", strerror(errno));
Packit Service 9e77c8
    return -1;
Packit Service 9e77c8
  }
Packit Service 9e77c8
  while ( len && ((rc=read(fd, (char*)dst+bytes_read, len)) > 0)) {
Packit Service 9e77c8
    len -= rc;
Packit Service 9e77c8
    bytes_read += rc;
Packit Service 9e77c8
  }
Packit Service 9e77c8
  if (rc==-1) vv("read_mem failed (%s)\n",strerror(errno));
Packit Service 9e77c8
  if ((len != 0 && rc >= 0)) vv("INTERNAL ERROR\n");
Packit Service 9e77c8
  return (rc == -1) ? -1 : 0;
Packit Service 9e77c8
}
Packit Service 9e77c8
#endif
Packit Service 9e77c8
Packit Service 9e77c8
/* later compensate for possible presence of bloom filter */
Packit Service 9e77c8
char *tbl_from_sig_addr(char *sig) {
Packit Service 9e77c8
  return (sig - offsetof(UT_hash_table,signature));
Packit Service 9e77c8
}
Packit Service 9e77c8
Packit Service 9e77c8
#define HS_BIT_TEST(v,i) (v[i/8] & (1U << (i%8)))
Packit Service 9e77c8
void found(int fd, char* peer_sig, pid_t pid) {
Packit Service 9e77c8
  UT_hash_table *tbl=NULL;
Packit Service 9e77c8
  UT_hash_bucket *bkts=NULL;
Packit Service 9e77c8
  UT_hash_handle hh;
Packit Service 9e77c8
  size_t i, bloom_len, bloom_bitlen,  bloom_on_bits=0,bloom_off_bits=0;
Packit Service 9e77c8
  char *peer_tbl, *peer_bloom_sig, *peer_bloom_nbits, *peer_bloombv_ptr, 
Packit Service 9e77c8
       *peer_bloombv, *peer_bkts, *peer_key, *peer_hh, *key=NULL,
Packit Service 9e77c8
       *hash_fcn=NULL, sat[10];
Packit Service 9e77c8
  unsigned char *bloombv=NULL; 
Packit Service 9e77c8
  static int fileno=0;
Packit Service 9e77c8
  char keyfile[50];
Packit Service 9e77c8
  unsigned char bloom_nbits=0;
Packit Service 9e77c8
  int keyfd=-1, mode=S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH,
Packit Service 9e77c8
      hash_fcn_hits[NUM_HASH_FUNCS], hash_fcn_winner;
Packit Service 9e77c8
  unsigned max_chain=0;
Packit Service 9e77c8
  uint32_t bloomsig;
Packit Service 9e77c8
  double bloom_sat=0;
Packit Service 9e77c8
  snprintf(sat,sizeof(sat),"         ");
Packit Service 9e77c8
  for(i=0; i < NUM_HASH_FUNCS; i++) hash_fcn_hits[i]=0;
Packit Service 9e77c8
Packit Service 9e77c8
  if (getkeys) {
Packit Service 9e77c8
    snprintf(keyfile, sizeof(keyfile), "/tmp/%u-%u.key", (unsigned)pid,fileno++);
Packit Service 9e77c8
    if ( (keyfd = open(keyfile, O_WRONLY|O_CREAT|O_TRUNC, mode)) == -1) {
Packit Service 9e77c8
      fprintf(stderr, "can't open %s: %s\n", keyfile, strerror(errno));
Packit Service 9e77c8
      exit(-1);
Packit Service 9e77c8
    }
Packit Service 9e77c8
  }
Packit Service 9e77c8
Packit Service 9e77c8
  vv("found signature at peer %p\n", peer_sig);
Packit Service 9e77c8
  peer_tbl = tbl_from_sig_addr(peer_sig);
Packit Service 9e77c8
  vvv("reading table at peer %p\n", peer_tbl);
Packit Service 9e77c8
Packit Service 9e77c8
  if ( (tbl = (UT_hash_table*)malloc(sizeof(UT_hash_table))) == NULL) {
Packit Service 9e77c8
    fprintf(stderr, "out of memory\n");
Packit Service 9e77c8
    exit(-1);
Packit Service 9e77c8
  }
Packit Service 9e77c8
#ifdef __FreeBSD__
Packit Service 9e77c8
  if (read_mem(tbl, pid, (void *)peer_tbl, sizeof(UT_hash_table)) != 0) {
Packit Service 9e77c8
#else
Packit Service 9e77c8
  if (read_mem(tbl, fd, (off_t)peer_tbl, sizeof(UT_hash_table)) != 0) {
Packit Service 9e77c8
#endif
Packit Service 9e77c8
    fprintf(stderr, "failed to read peer memory\n");
Packit Service 9e77c8
    goto done;
Packit Service 9e77c8
  }
Packit Service 9e77c8
Packit Service 9e77c8
  /* got the table. how about the buckets */
Packit Service 9e77c8
  peer_bkts = (char*)tbl->buckets;
Packit Service 9e77c8
  vvv("reading buckets at peer %p\n", peer_bkts);
Packit Service 9e77c8
  bkts = (UT_hash_bucket*)malloc(sizeof(UT_hash_bucket)*tbl->num_buckets);
Packit Service 9e77c8
  if (bkts == NULL) {
Packit Service 9e77c8
    fprintf(stderr, "out of memory\n");
Packit Service 9e77c8
    exit(-1);
Packit Service 9e77c8
  }
Packit Service 9e77c8
#ifdef __FreeBSD__
Packit Service 9e77c8
  if (read_mem(bkts, pid, (void *)peer_bkts, sizeof(UT_hash_bucket)*tbl->num_buckets) != 0) {
Packit Service 9e77c8
#else
Packit Service 9e77c8
  if (read_mem(bkts, fd, (off_t)peer_bkts, sizeof(UT_hash_bucket)*tbl->num_buckets) != 0) {
Packit Service 9e77c8
#endif
Packit Service 9e77c8
    fprintf(stderr, "failed to read peer memory\n");
Packit Service 9e77c8
    goto done;
Packit Service 9e77c8
  }
Packit Service 9e77c8
Packit Service 9e77c8
  vvv("scanning %u peer buckets\n", tbl->num_buckets);
Packit Service 9e77c8
  for(i=0; i < tbl->num_buckets; i++) {
Packit Service 9e77c8
    vvv("bucket %u has %u items\n",  (unsigned)i, (unsigned)(bkts[i].count));
Packit Service 9e77c8
    if (bkts[i].count > max_chain) max_chain = bkts[i].count;
Packit Service 9e77c8
    if (bkts[i].expand_mult) vvv("  bucket %u has expand_mult %u\n",  (unsigned)i, (unsigned)(bkts[i].expand_mult));
Packit Service 9e77c8
Packit Service 9e77c8
    vvv("scanning bucket %u chain:\n",  (unsigned)i);
Packit Service 9e77c8
    peer_hh = (char*)bkts[i].hh_head;
Packit Service 9e77c8
    while(peer_hh) {
Packit Service 9e77c8
#ifdef __FreeBSD__
Packit Service 9e77c8
      if (read_mem(&hh, pid, (void *)peer_hh, sizeof(hh)) != 0) {
Packit Service 9e77c8
#else
Packit Service 9e77c8
      if (read_mem(&hh, fd, (off_t)peer_hh, sizeof(hh)) != 0) {
Packit Service 9e77c8
#endif
Packit Service 9e77c8
        fprintf(stderr, "failed to read peer memory\n");
Packit Service 9e77c8
        goto done;
Packit Service 9e77c8
      }
Packit Service 9e77c8
      if ((char*)hh.tbl != peer_tbl) goto done;
Packit Service 9e77c8
      peer_hh = (char*)hh.hh_next;
Packit Service 9e77c8
      peer_key = (char*)(hh.key);
Packit Service 9e77c8
      /* malloc space to read the key, and read it */
Packit Service 9e77c8
      if ( (key = (char*)malloc(sizeof(hh.keylen))) == NULL) {
Packit Service 9e77c8
        fprintf(stderr, "out of memory\n");
Packit Service 9e77c8
        exit(-1);
Packit Service 9e77c8
      }
Packit Service 9e77c8
#ifdef __FreeBSD__
Packit Service 9e77c8
      if (read_mem(key, pid, (void*)peer_key, hh.keylen) != 0) {
Packit Service 9e77c8
#else
Packit Service 9e77c8
      if (read_mem(key, fd, (off_t)peer_key, hh.keylen) != 0) {
Packit Service 9e77c8
#endif
Packit Service 9e77c8
        fprintf(stderr, "failed to read peer memory\n");
Packit Service 9e77c8
        goto done;
Packit Service 9e77c8
      }
Packit Service 9e77c8
      hash_fcn_hits[infer_hash_function(key,hh.keylen,hh.hashv)]++;
Packit Service 9e77c8
      /* write the key if requested */
Packit Service 9e77c8
      if (getkeys) {
Packit Service 9e77c8
        write(keyfd, &hh.keylen, sizeof(unsigned));
Packit Service 9e77c8
        write(keyfd, key, hh.keylen);
Packit Service 9e77c8
      }
Packit Service 9e77c8
      free(key); key=NULL;
Packit Service 9e77c8
    }
Packit Service 9e77c8
  }
Packit Service 9e77c8
Packit Service 9e77c8
  /* does it have a bloom filter?  */
Packit Service 9e77c8
  peer_bloom_sig =   peer_tbl + offsetof(UT_hash_table, bloom_sig);
Packit Service 9e77c8
  peer_bloombv_ptr = peer_tbl + offsetof(UT_hash_table, bloom_bv);
Packit Service 9e77c8
  peer_bloom_nbits = peer_tbl + offsetof(UT_hash_table, bloom_nbits);
Packit Service 9e77c8
  vvv("looking for bloom signature at peer %p\n", peer_bloom_sig);
Packit Service 9e77c8
#ifdef __FreeBSD__
Packit Service 9e77c8
  if ((read_mem(&bloomsig, pid, (void *)peer_bloom_sig, sizeof(uint32_t)) == 0)  &&
Packit Service 9e77c8
      (bloomsig == HASH_BLOOM_SIGNATURE)) {
Packit Service 9e77c8
#else
Packit Service 9e77c8
  if ((read_mem(&bloomsig, fd, (off_t)peer_bloom_sig, sizeof(uint32_t)) == 0)  &&
Packit Service 9e77c8
      (bloomsig == HASH_BLOOM_SIGNATURE)) {
Packit Service 9e77c8
#endif
Packit Service 9e77c8
    vvv("bloom signature (%x) found\n",bloomsig);
Packit Service 9e77c8
    /* bloom found. get at bv, nbits */
Packit Service 9e77c8
#ifdef __FreeBSD__
Packit Service 9e77c8
    if (read_mem(&bloom_nbits, pid, (void *)peer_bloom_nbits, sizeof(char)) == 0) {
Packit Service 9e77c8
#else
Packit Service 9e77c8
    if (read_mem(&bloom_nbits, fd, (off_t)peer_bloom_nbits, sizeof(char)) == 0) {
Packit Service 9e77c8
#endif
Packit Service 9e77c8
       /* scan bloom filter, calculate saturation */
Packit Service 9e77c8
       bloom_bitlen = (1ULL << bloom_nbits);
Packit Service 9e77c8
       bloom_len = (bloom_bitlen / 8) + ((bloom_bitlen % 8) ? 1 : 0);
Packit Service 9e77c8
       vvv("bloom bitlen is %u, bloom_bytelen is %u\n", (unsigned)bloom_bitlen, (unsigned)bloom_len);
Packit Service 9e77c8
       if ( (bloombv = (unsigned char*)malloc(bloom_len)) == NULL) {
Packit Service 9e77c8
          fprintf(stderr, "out of memory\n");
Packit Service 9e77c8
          exit(-1);
Packit Service 9e77c8
       }
Packit Service 9e77c8
       /* read the address of the bitvector in the peer, then read the bv itself */
Packit Service 9e77c8
#ifdef __FreeBSD__
Packit Service 9e77c8
       if ((read_mem(&peer_bloombv, pid, (void *)peer_bloombv_ptr, sizeof(void*)) == 0) &&
Packit Service 9e77c8
          (read_mem(bloombv, pid, (void *)peer_bloombv, bloom_len) == 0)) {
Packit Service 9e77c8
#else
Packit Service 9e77c8
       if ((read_mem(&peer_bloombv, fd, (off_t)peer_bloombv_ptr, sizeof(void*)) == 0) && 
Packit Service 9e77c8
          (read_mem(bloombv, fd, (off_t)peer_bloombv, bloom_len) == 0)) {
Packit Service 9e77c8
#endif
Packit Service 9e77c8
          /* calculate saturation */
Packit Service 9e77c8
          vvv("read peer bloom bitvector from %p (%u bytes)\n", peer_bloombv, (unsigned)bloom_len);
Packit Service 9e77c8
          for(i=0; i < bloom_bitlen; i++) {
Packit Service 9e77c8
              if (HS_BIT_TEST(bloombv,(unsigned)i)) {
Packit Service 9e77c8
                /* vvv("bit %u set\n",(unsigned)i); */
Packit Service 9e77c8
                bloom_on_bits++;
Packit Service 9e77c8
              } else bloom_off_bits++;
Packit Service 9e77c8
          }
Packit Service 9e77c8
          vvv("there were %u on_bits among %u total bits\n", (unsigned)bloom_on_bits, (unsigned)bloom_bitlen);
Packit Service 9e77c8
          bloom_sat = bloom_on_bits * 100.0 / bloom_bitlen;
Packit Service 9e77c8
          snprintf(sat,sizeof(sat),"%2u %5.0f%%", bloom_nbits, bloom_sat);
Packit Service 9e77c8
       }
Packit Service 9e77c8
    }
Packit Service 9e77c8
  }
Packit Service 9e77c8
Packit Service 9e77c8
  /* choose apparent hash function */
Packit Service 9e77c8
  hash_fcn_winner=0;
Packit Service 9e77c8
  for(i=0;i
Packit Service 9e77c8
    if (hash_fcn_hits[i] > hash_fcn_hits[hash_fcn_winner]) hash_fcn_winner=i;
Packit Service 9e77c8
  }
Packit Service 9e77c8
  hash_fcn = hash_fcns[hash_fcn_winner];
Packit Service 9e77c8
Packit Service 9e77c8
/*
Packit Service 9e77c8
Address            items    ideal  buckets mxch/<10 fl bloom/sat fcn keys saved to
Packit Service 9e77c8
------------------ -------- ----- -------- -------- -- --------- --- -------------
Packit Service 9e77c8
0x0123456789abcdef 10000000  98%  32000000 10  100% ok           BER /tmp/9110-0.key
Packit Service 9e77c8
0x0123456789abcdef 10000000 100%  32000000  9   90% NX 27/0.010% BER /tmp/9110-1.key
Packit Service 9e77c8
*/
Packit Service 9e77c8
  printf("Address            ideal    items  buckets mc fl bloom/sat fcn keys saved to\n");
Packit Service 9e77c8
  printf("------------------ ----- -------- -------- -- -- --------- --- -------------\n");
Packit Service 9e77c8
  printf("%-18p %4.0f%% %8u %8u %2u %s %s %s %s\n",
Packit Service 9e77c8
    (void*)peer_tbl, 
Packit Service 9e77c8
    (tbl->num_items - tbl->nonideal_items) * 100.0 / tbl->num_items,
Packit Service 9e77c8
    tbl->num_items,
Packit Service 9e77c8
    tbl->num_buckets,
Packit Service 9e77c8
    max_chain,
Packit Service 9e77c8
    tbl->noexpand ? "NX" : "ok",
Packit Service 9e77c8
    sat,
Packit Service 9e77c8
    hash_fcn,
Packit Service 9e77c8
    (getkeys ? keyfile : ""));
Packit Service 9e77c8
Packit Service 9e77c8
#if 0
Packit Service 9e77c8
  printf("read peer tbl:\n");
Packit Service 9e77c8
  printf("num_buckets: %u\n", tbl->num_buckets); 
Packit Service 9e77c8
  printf("num_items: %u\n", tbl->num_items); 
Packit Service 9e77c8
  printf("nonideal_items: %u (%.2f%%)\n", tbl->nonideal_items, 
Packit Service 9e77c8
    tbl->nonideal_items*100.0/tbl->num_items); 
Packit Service 9e77c8
  printf("expand: %s\n", tbl->noexpand ? "inhibited": "normal"); 
Packit Service 9e77c8
  if (getkeys) printf("keys written to %s\n", keyfile);
Packit Service 9e77c8
#endif
Packit Service 9e77c8
Packit Service 9e77c8
 done:
Packit Service 9e77c8
  if (bkts) free(bkts);
Packit Service 9e77c8
  if (tbl) free(tbl);
Packit Service 9e77c8
  if (key) free(key);
Packit Service 9e77c8
  if (keyfd != -1) close(keyfd);
Packit Service 9e77c8
  if (bloombv) free(bloombv);
Packit Service 9e77c8
}
Packit Service 9e77c8
Packit Service 9e77c8
Packit Service 9e77c8
#ifdef __FreeBSD__
Packit Service 9e77c8
void sigscan(pid_t pid, void *start, void *end, uint32_t sig) {
Packit Service 9e77c8
  struct ptrace_io_desc io_desc;
Packit Service 9e77c8
  int page_size = getpagesize();
Packit Service 9e77c8
  char *buf;
Packit Service 9e77c8
  char *pos;
Packit Service 9e77c8
Packit Service 9e77c8
  /* make sure page_size is a multiple of the signature size, code below assumes this */
Packit Service 9e77c8
  assert(page_size % sizeof(sig) == 0);
Packit Service 9e77c8
Packit Service 9e77c8
  buf = malloc(page_size);
Packit Service 9e77c8
Packit Service 9e77c8
  if (buf == NULL) {
Packit Service 9e77c8
	fprintf(stderr, "malloc failed in sigscan()\n");
Packit Service 9e77c8
	return;
Packit Service 9e77c8
  }
Packit Service 9e77c8
Packit Service 9e77c8
  io_desc.piod_op = PIOD_READ_D;
Packit Service 9e77c8
  io_desc.piod_offs = start;
Packit Service 9e77c8
  io_desc.piod_addr = buf;
Packit Service 9e77c8
  io_desc.piod_len = page_size;
Packit Service 9e77c8
Packit Service 9e77c8
  /* read in one page after another and search sig */
Packit Service 9e77c8
  while(!ptrace(PT_IO, pid, (void *) &io_desc, 0)) {
Packit Service 9e77c8
    if (io_desc.piod_len != page_size) {
Packit Service 9e77c8
      fprintf(stderr, "PT_IO returned less than page size in sigscan()\n");
Packit Service 9e77c8
      return;
Packit Service 9e77c8
    }
Packit Service 9e77c8
    
Packit Service 9e77c8
    /* iterate over the the page using the signature size and look for the sig */
Packit Service 9e77c8
    for (pos = buf; pos < (buf + page_size); pos += sizeof(sig)) {
Packit Service 9e77c8
      if (*(uint32_t *) pos == sig) {
Packit Service 9e77c8
        found(pid, (char *) io_desc.piod_offs + (pos - buf), pid);
Packit Service 9e77c8
      }
Packit Service 9e77c8
    }
Packit Service 9e77c8
    
Packit Service 9e77c8
    /* 
Packit Service 9e77c8
     * 'end' is inclusive (the address of the last valid byte), so if the current offset
Packit Service 9e77c8
     * plus a page is beyond 'end', we're already done. since all vm map entries consist
Packit Service 9e77c8
     * of entire pages and 'end' is inclusive, current offset plus one page should point 
Packit Service 9e77c8
     * exactly one byte beyond 'end'. this is assert()ed below to be on the safe side.
Packit Service 9e77c8
     */
Packit Service 9e77c8
    if (io_desc.piod_offs + page_size > end) {
Packit Service 9e77c8
      assert(io_desc.piod_offs + page_size == (end + 1));
Packit Service 9e77c8
      break;
Packit Service 9e77c8
    }
Packit Service 9e77c8
    
Packit Service 9e77c8
    /* advance to the next page */
Packit Service 9e77c8
    io_desc.piod_offs += page_size;
Packit Service 9e77c8
  }
Packit Service 9e77c8
}
Packit Service 9e77c8
#else
Packit Service 9e77c8
void sigscan(int fd, off_t start, off_t end, uint32_t sig, pid_t pid) {
Packit Service 9e77c8
  int rlen;
Packit Service 9e77c8
  uint32_t u;
Packit Service 9e77c8
  off_t at=0;
Packit Service 9e77c8
Packit Service 9e77c8
  if (lseek(fd, start, SEEK_SET) == (off_t)-1) {
Packit Service 9e77c8
    fprintf(stderr, "lseek failed: %s\n", strerror(errno));
Packit Service 9e77c8
    return;
Packit Service 9e77c8
  }
Packit Service 9e77c8
Packit Service 9e77c8
  while ( (rlen = read(fd,&u,sizeof(u))) == sizeof(u)) {
Packit Service 9e77c8
     if (!memcmp(&u,&sig,sizeof(u))) found(fd, (char*)(start+at),pid);
Packit Service 9e77c8
     at += sizeof(u);
Packit Service 9e77c8
     if ((off_t)(at + sizeof(u)) > end-start) break;
Packit Service 9e77c8
  }
Packit Service 9e77c8
Packit Service 9e77c8
  if (rlen == -1) {
Packit Service 9e77c8
    //fprintf(stderr,"read failed: %s\n", strerror(errno));
Packit Service 9e77c8
    //exit(-1);
Packit Service 9e77c8
  }
Packit Service 9e77c8
}
Packit Service 9e77c8
#endif
Packit Service 9e77c8
Packit Service 9e77c8
Packit Service 9e77c8
#ifdef __FreeBSD__
Packit Service 9e77c8
int scan(pid_t pid) {
Packit Service 9e77c8
  vma_t *vmas=NULL, vma;
Packit Service 9e77c8
  unsigned i, num_vmas = 0;
Packit Service 9e77c8
  int ret;
Packit Service 9e77c8
  struct ptrace_vm_entry vm_entry;
Packit Service 9e77c8
  char path[MAXPATHLEN];
Packit Service 9e77c8
Packit Service 9e77c8
  vv("attaching to peer\n");
Packit Service 9e77c8
  if (ptrace(PT_ATTACH,pid,NULL,0) == -1) {
Packit Service 9e77c8
    fprintf(stderr,"failed to attach to %u: %s\n", (unsigned)pid, strerror(errno));
Packit Service 9e77c8
    exit(EXIT_FAILURE);
Packit Service 9e77c8
  }
Packit Service 9e77c8
  vv("waiting for peer to suspend temporarily\n");
Packit Service 9e77c8
  if (waitpid(pid,NULL,0) != pid) {
Packit Service 9e77c8
    fprintf(stderr,"failed to wait for pid %u: %s\n",(unsigned)pid, strerror(errno));
Packit Service 9e77c8
    goto die;
Packit Service 9e77c8
  }
Packit Service 9e77c8
  
Packit Service 9e77c8
  /* read memory map using ptrace */
Packit Service 9e77c8
  vv("listing peer virtual memory areas\n");
Packit Service 9e77c8
  vm_entry.pve_entry = 0;
Packit Service 9e77c8
  vm_entry.pve_path = path; /* not used but required to make vm_entry.pve_pathlen work */
Packit Service 9e77c8
  while(1) {
Packit Service 9e77c8
    /* set pve_pathlen every turn, it gets overwritten by ptrace */
Packit Service 9e77c8
    vm_entry.pve_pathlen = MAXPATHLEN;
Packit Service 9e77c8
    errno = 0;
Packit Service 9e77c8
    
Packit Service 9e77c8
    ret = ptrace(PT_VM_ENTRY, pid, (void *) &vm_entry, 0);
Packit Service 9e77c8
    
Packit Service 9e77c8
    if (ret) {
Packit Service 9e77c8
      if (errno == ENOENT) {
Packit Service 9e77c8
        /* we've reached the last entry */
Packit Service 9e77c8
	break;
Packit Service 9e77c8
      }
Packit Service 9e77c8
      fprintf(stderr, "fetching vm map entry failed: %s (%i)\n", strerror(errno), errno);
Packit Service 9e77c8
      goto die;
Packit Service 9e77c8
    }
Packit Service 9e77c8
    
Packit Service 9e77c8
    vvv("vmmap entry: start: %p, end: %p", (void *) vm_entry.pve_start, (void *) vm_entry.pve_end);
Packit Service 9e77c8
    
Packit Service 9e77c8
    /* skip unreadable or vnode-backed entries */
Packit Service 9e77c8
    if (!(vm_entry.pve_prot & VM_PROT_READ) || vm_entry.pve_pathlen > 0) {
Packit Service 9e77c8
      vvv(" -> skipped (not readable or vnode-backed)\n");
Packit Service 9e77c8
      vm_entry.pve_path[0] = 0;
Packit Service 9e77c8
      continue;
Packit Service 9e77c8
    }
Packit Service 9e77c8
Packit Service 9e77c8
    /* useful entry, add to list */
Packit Service 9e77c8
    vvv(" -> will be scanned\n");
Packit Service 9e77c8
    vma.start = (void *)vm_entry.pve_start;
Packit Service 9e77c8
    vma.end = (void *)vm_entry.pve_end;
Packit Service 9e77c8
    vmas = (vma_t *) realloc(vmas, (num_vmas + 1) * sizeof(vma_t));
Packit Service 9e77c8
    vmas[num_vmas++] = vma;
Packit Service 9e77c8
  }
Packit Service 9e77c8
Packit Service 9e77c8
  vv("peer has %u virtual memory areas\n", num_vmas);
Packit Service 9e77c8
  
Packit Service 9e77c8
  /* look for the hash signature */
Packit Service 9e77c8
  vv("scanning peer memory for hash table signatures\n");
Packit Service 9e77c8
  for(i=0;i
Packit Service 9e77c8
    vma = vmas[i];
Packit Service 9e77c8
    sigscan(pid, vma.start, vma.end, sig);
Packit Service 9e77c8
  }
Packit Service 9e77c8
 
Packit Service 9e77c8
die:
Packit Service 9e77c8
  vv("detaching and resuming peer\n");
Packit Service 9e77c8
  if (ptrace(PT_DETACH, pid, NULL, 0) == -1) {
Packit Service 9e77c8
    fprintf(stderr,"failed to detach from %u: %s\n", (unsigned)pid, strerror(errno));
Packit Service 9e77c8
  }
Packit Service 9e77c8
  return 0;
Packit Service 9e77c8
}
Packit Service 9e77c8
# else
Packit Service 9e77c8
int scan(pid_t pid) {
Packit Service 9e77c8
  FILE *mapf;
Packit Service 9e77c8
  char mapfile[30], memfile[30], line[100];
Packit Service 9e77c8
  vma_t *vmas=NULL, vma;
Packit Service 9e77c8
  unsigned i, num_vmas = 0;
Packit Service 9e77c8
  int memfd;
Packit Service 9e77c8
  void *pstart, *pend, *unused;
Packit Service 9e77c8
  
Packit Service 9e77c8
  /* attach to the target process and wait for it to suspend */
Packit Service 9e77c8
  vv("attaching to peer\n");
Packit Service 9e77c8
  if (ptrace(PTRACE_ATTACH,pid,NULL,NULL) == -1) {
Packit Service 9e77c8
    fprintf(stderr,"failed to attach to %u: %s\n", (unsigned)pid, strerror(errno));
Packit Service 9e77c8
    exit(-1);
Packit Service 9e77c8
  }
Packit Service 9e77c8
  vv("waiting for peer to suspend temporarily\n");
Packit Service 9e77c8
  if (waitpid(pid,NULL,0) != pid) {
Packit Service 9e77c8
    fprintf(stderr,"failed to wait for pid %u: %s\n",(unsigned)pid, strerror(errno));
Packit Service 9e77c8
    goto die;
Packit Service 9e77c8
  }
Packit Service 9e77c8
Packit Service 9e77c8
  /* get ready to open its memory map. this gives us its valid memory areas */
Packit Service 9e77c8
  snprintf(mapfile,sizeof(mapfile),"/proc/%u/maps",(unsigned)pid);
Packit Service 9e77c8
  snprintf(memfile,sizeof(memfile),"/proc/%u/mem", (unsigned)pid);
Packit Service 9e77c8
  vv("opening peer memory map [%s]\n", mapfile);
Packit Service 9e77c8
  if ( (mapf = fopen(mapfile,"r")) == NULL) {
Packit Service 9e77c8
    fprintf(stderr,"failed to open %s: %s\n", mapfile, strerror(errno));
Packit Service 9e77c8
    goto die;
Packit Service 9e77c8
  }
Packit Service 9e77c8
  vv("listing peer virtual memory areas\n");
Packit Service 9e77c8
  while(fgets(line,sizeof(line),mapf)) {
Packit Service 9e77c8
    if (sscanf(line, "%p-%p %4c %p %5c", &pstart, &pend, vma.perms,
Packit Service 9e77c8
         &unused, vma.device) == 5) {
Packit Service 9e77c8
      vma.start = (off_t)pstart;
Packit Service 9e77c8
      vma.end = (off_t)pend;
Packit Service 9e77c8
      if (vma.perms[0] != 'r') continue;          /* only readable vma's */
Packit Service 9e77c8
      if (memcmp(vma.device,"fd",2)==0) continue; /* skip mapped files */
Packit Service 9e77c8
      vmas = (vma_t*)realloc(vmas, (num_vmas+1) * sizeof(vma_t));
Packit Service 9e77c8
      vmas[num_vmas++] = vma;
Packit Service 9e77c8
    }
Packit Service 9e77c8
  }
Packit Service 9e77c8
  vv("peer has %u virtual memory areas\n",num_vmas);
Packit Service 9e77c8
  fclose(mapf);
Packit Service 9e77c8
Packit Service 9e77c8
  /* ok, open up its memory and start looking around in there */
Packit Service 9e77c8
  vv("opening peer memory\n");
Packit Service 9e77c8
  if ( (memfd=open(memfile,O_RDONLY)) == -1) {
Packit Service 9e77c8
    fprintf(stderr,"failed to open %s: %s\n", memfile, strerror(errno));
Packit Service 9e77c8
    goto die;
Packit Service 9e77c8
  }
Packit Service 9e77c8
  /* look for the hash signature */
Packit Service 9e77c8
  vv("scanning peer memory for hash table signatures\n");
Packit Service 9e77c8
  for(i=0;i
Packit Service 9e77c8
    vma = vmas[i];
Packit Service 9e77c8
    pstart = (void*)vma.start;
Packit Service 9e77c8
    pend = (void*)vma.end;
Packit Service 9e77c8
    /*fprintf(stderr,"scanning %p-%p %.4s %.5s\n", pstart, pend, 
Packit Service 9e77c8
              vma.perms, vma.device);*/
Packit Service 9e77c8
    sigscan(memfd, vma.start, vma.end, sig, pid);
Packit Service 9e77c8
  }
Packit Service 9e77c8
Packit Service 9e77c8
  /* done. close memory and detach. this resumes the target process */
Packit Service 9e77c8
  close(memfd);
Packit Service 9e77c8
Packit Service 9e77c8
 die:
Packit Service 9e77c8
  vv("detaching and resuming peer\n");
Packit Service 9e77c8
  if (ptrace(PTRACE_DETACH, pid, NULL, NULL) == -1) {
Packit Service 9e77c8
    fprintf(stderr,"failed to detach from %u: %s\n", (unsigned)pid, strerror(errno));
Packit Service 9e77c8
  }
Packit Service 9e77c8
  return 0;
Packit Service 9e77c8
}
Packit Service 9e77c8
#endif
Packit Service 9e77c8
Packit Service 9e77c8
Packit Service 9e77c8
void usage(const char *prog) {
Packit Service 9e77c8
  fprintf(stderr,"usage: %s [-v] [-k] <pid>\n", prog);
Packit Service 9e77c8
  exit(-1);
Packit Service 9e77c8
}
Packit Service 9e77c8
Packit Service 9e77c8
int main(int argc, char *argv[]) {
Packit Service 9e77c8
  pid_t pid;
Packit Service 9e77c8
  int opt;
Packit Service 9e77c8
Packit Service 9e77c8
  while ( (opt = getopt(argc, argv, "kv")) != -1) {
Packit Service 9e77c8
    switch (opt) {
Packit Service 9e77c8
      case 'v':
Packit Service 9e77c8
        verbose++;
Packit Service 9e77c8
        break;
Packit Service 9e77c8
      case 'k':
Packit Service 9e77c8
        getkeys++;
Packit Service 9e77c8
        break;
Packit Service 9e77c8
      default:
Packit Service 9e77c8
        usage(argv[0]);
Packit Service 9e77c8
        break;
Packit Service 9e77c8
    }
Packit Service 9e77c8
  }
Packit Service 9e77c8
 
Packit Service 9e77c8
  if (optind < argc) pid=atoi(argv[optind++]);
Packit Service 9e77c8
  else usage(argv[0]);
Packit Service 9e77c8
Packit Service 9e77c8
  return scan(pid);
Packit Service 9e77c8
}