<commit_before>#include <unistd.h>

#include "txn_btree.h"
#include "thread.h"
#include "util.h"

#define VERBOSE(expr) ((void)0)

using namespace std;
using namespace util;

bool
txn_btree::search(transaction &t, key_type k, value_type &v)
{
  assert(!t.btree || t.btree == this);
  t.btree = this;

  // priority is
  // 1) write set
  // 2) local read set
  // 3) range set
  // 4) query underlying tree
  //
  // note (1)-(3) are served by transaction::local_search()

  if (t.local_search(k, v))
    return (bool) v;

  btree::value_type underlying_v;
  if (!underlying_btree.search(k, underlying_v)) {
    // all records exist in the system at MIN_TID with no value
    transaction::read_record_t *read_rec = &t.read_set[k];
    read_rec->t = transaction::MIN_TID;
    read_rec->r = NULL;
    read_rec->ln = NULL;
    return false;
  } else {
    transaction::logical_node *ln = (transaction::logical_node *) underlying_v;
    assert(ln);
    transaction::tid_t start_t;
    transaction::record_t r;
    if (unlikely(!ln->stable_read(t.snapshot_tid, start_t, r))) {
      t.abort();
      throw transaction_abort_exception();
    }
    transaction::read_record_t *read_rec = &t.read_set[k];
    read_rec->t = start_t;
    read_rec->r = r;
    read_rec->ln = ln;
    v = read_rec->r;
    return read_rec->r;
  }
}

bool
txn_btree::txn_search_range_callback::invoke(key_type k, value_type v)
{
  VERBOSE(cerr << "search range k: " << k << endl);
  transaction::key_range_t r(invoked ? prev_key : lower, k);
  if (!r.is_empty_range())
    t->add_absent_range(r);
  prev_key = k;
  invoked = true;
  value_type local_v = 0;
  bool local_read = t->local_search(k, local_v);
  bool ret = true;
  if (local_read && local_v)
    ret = caller_callback->invoke(k, local_v);
  map<key_type, transaction::read_record_t>::const_iterator it =
    t->read_set.find(k);
  if (it == t->read_set.end()) {
    transaction::logical_node *ln = (transaction::logical_node *) v;
    assert(ln);
    transaction::tid_t start_t;
    transaction::record_t r;
    if (unlikely(!ln->stable_read(t->snapshot_tid, start_t, r))) {
      t->abort();
      throw transaction_abort_exception();
    }
    transaction::read_record_t *read_rec = &t->read_set[k];
    read_rec->t = start_t;
    read_rec->r = r;
    read_rec->ln = ln;
    VERBOSE(cerr << "read <t=" << start_t << ", r=" << size_t(r) << "> (local_read=" << local_read << ")" << endl);
    if (!local_read && r)
      ret = caller_callback->invoke(k, r);
  }
  if (ret)
    caller_stopped = true;
  return ret;
}

bool
txn_btree::absent_range_validation_callback::invoke(key_type k, value_type v)
{
  transaction::logical_node *ln = (transaction::logical_node *) v;
  assert(ln);
  //cout << "absent_range_validation_callback: key " << k << " found ln " << intptr_t(ln) << endl;
  bool did_write = t->write_set.find(k) != t->write_set.end();
  failed_flag = did_write ? !ln->is_latest_version(0) : !ln->stable_is_latest_version(0);
  return !failed_flag;
}

void
txn_btree::search_range_call(transaction &t,
                             key_type lower,
                             const key_type *upper,
                             search_range_callback &callback)
{
  assert(!t.btree || t.btree == this);
  t.btree = this;

  // many cases to consider:
  // 1) for each logical_node returned from the scan, we need to
  //    record it in our local read set. there are several cases:
  //    A) if the logical_node corresponds to a key we have written, then
  //       we emit the version from the local write set
  //    B) if the logical_node corresponds to a key we have previous read,
  //       then we emit the previous version
  // 2) for each logical_node node *not* returned from the scan, we need
  //    to record its absense. we optimize this by recording the absense
  //    of contiguous ranges
  if (upper && *upper <= lower)
    return;
  txn_search_range_callback c(&t, lower, upper, &callback);
  underlying_btree.search_range_call(lower, upper, c);
  if (c.caller_stopped)
    return;
  if (c.invoked && c.prev_key == (key_type)-1)
    return;
  if (upper)
    t.add_absent_range(transaction::key_range_t(c.invoked ? (c.prev_key + 1): lower, *upper));
  else
    t.add_absent_range(transaction::key_range_t(c.invoked ? c.prev_key : lower));
}

void
txn_btree::insert_impl(transaction &t, key_type k, value_type v)
{
  assert(!t.btree || t.btree == this);
  t.btree = this;
  t.write_set[k] = v;
}

struct test_callback_ctr {
  test_callback_ctr(size_t *ctr) : ctr(ctr) {}

  inline bool
  operator()(txn_btree::key_type k, txn_btree::value_type v) const
  {
    (*ctr)++;
    return true;
  }
  size_t *ctr;
};

static void
test1()
{
  txn_btree btr;

  struct rec { uint64_t v; };
  rec recs[10];
  for (size_t i = 0; i < ARRAY_NELEMS(recs); i++)
    recs[i].v = 0;

  {
    transaction t;
    txn_btree::value_type v;
    ALWAYS_ASSERT(!btr.search(t, 0, v));
    btr.insert(t, 0, (txn_btree::value_type) &recs[0]);
    ALWAYS_ASSERT(btr.search(t, 0, v));
    ALWAYS_ASSERT(v == (txn_btree::value_type) &recs[0]);
    t.commit();
    VERBOSE(cout << "------" << endl);
  }

  {
    transaction t0, t1;
    txn_btree::value_type v0, v1;

    ALWAYS_ASSERT(btr.search(t0, 0, v0));
    ALWAYS_ASSERT(v0 == (txn_btree::value_type) &recs[0]);

    btr.insert(t0, 0, (txn_btree::value_type) &recs[1]);

    ALWAYS_ASSERT(btr.search(t1, 0, v1));
    ALWAYS_ASSERT(v1 == (txn_btree::value_type) &recs[0]);

    t0.commit();
    t1.commit();
    VERBOSE(cout << "------" << endl);
  }

  {
    // racy insert
    transaction t0, t1;
    txn_btree::value_type v0, v1;

    ALWAYS_ASSERT(btr.search(t0, 0, v0));
    ALWAYS_ASSERT(v0 == (txn_btree::value_type) &recs[1]);
    btr.insert(t0, 0, (txn_btree::value_type) &recs[2]);

    ALWAYS_ASSERT(btr.search(t1, 0, v1));
    ALWAYS_ASSERT(v1 == (txn_btree::value_type) &recs[1]);
    btr.insert(t1, 0, (txn_btree::value_type) &recs[3]);

    t0.commit(); // succeeds

    try {
      t1.commit(); // fails
      ALWAYS_ASSERT(false);
    } catch (transaction_abort_exception &e) {

    }
    VERBOSE(cout << "------" << endl);
  }

  {
    // racy scan
    transaction t0, t1;

    txn_btree::key_type vend = 5;
    size_t ctr = 0;
    btr.search_range(t0, 1, &vend, test_callback_ctr(&ctr));
    ALWAYS_ASSERT(ctr == 0);

    btr.insert(t1, 2, (txn_btree::value_type) &recs[4]);
    t1.commit();

    btr.insert(t0, 0, (txn_btree::value_type) &recs[0]);
    try {
      t0.commit(); // fails
      ALWAYS_ASSERT(false);
    } catch (transaction_abort_exception &e) {

    }
    VERBOSE(cout << "------" << endl);
  }

  {
    transaction t;
    txn_btree::key_type vend = 20;
    size_t ctr = 0;
    btr.search_range(t, 10, &vend, test_callback_ctr(&ctr));
    ALWAYS_ASSERT(ctr == 0);
    btr.insert(t, 15, (txn_btree::value_type) &recs[5]);
    t.commit();
    VERBOSE(cout << "------" << endl);
  }
}

static void
test2()
{
  txn_btree btr;
  for (size_t i = 0; i < 100; i++) {
    transaction t;
    btr.insert(t, 0, (txn_btree::value_type) 123);
    t.commit();
  }
}

class txn_btree_worker : public ndb_thread {
public:
  txn_btree_worker(txn_btree &btr) : btr(&btr) {}
protected:
  txn_btree *btr;
};

namespace mp_test1_ns {
  // read-modify-write test (counters)

  struct record {
    record() : v(0) {}
    uint64_t v;
  };

  const size_t niters = 1000;

  class worker : public txn_btree_worker {
  public:
    worker(txn_btree &btr) : txn_btree_worker(btr) {}
    ~worker()
    {
      for (vector<record *>::iterator it = recs.begin();
           it != recs.end(); ++it)
        delete *it;
    }
    virtual void run()
    {
      for (size_t i = 0; i < niters; i++) {
      retry:
        transaction t;
        record *rec = new record;
        recs.push_back(rec);
        try {
          txn_btree::value_type v = 0;
          if (!btr->search(t, 0, v)) {
            rec->v = 1;
          } else {
            *rec = *((record *)v);
            rec->v++;
          }
          btr->insert(t, 0, (txn_btree::value_type) rec);
          t.commit();
        } catch (transaction_abort_exception &e) {
          goto retry;
        }
      }
    }
  private:
    vector<record *> recs;
  };

}

static void
mp_test1()
{
  using namespace mp_test1_ns;

  txn_btree btr;

  worker w0(btr);
  worker w1(btr);

  w0.start(); w1.start();
  w0.join(); w1.join();

  transaction t;
  txn_btree::value_type v = 0;
  ALWAYS_ASSERT(btr.search(t, 0, v));
  ALWAYS_ASSERT( ((record *) v)->v == (niters * 2) );
  t.commit();

}

namespace mp_test2_ns {

  static const size_t ctr_key     = 0;

  static const txn_btree::key_type range_begin = 100;
  static const txn_btree::key_type range_end   = 200;

  static volatile bool running = true;

  class mutate_worker : public txn_btree_worker {
  public:
    mutate_worker(txn_btree &btr) : txn_btree_worker(btr) {}
    virtual void run()
    {
      while (running) {
        for (size_t i = range_begin; running && i < range_end; i++) {
        retry:
          transaction t;
          try {
            txn_btree::value_type v = 0, v_ctr = 0;
            ALWAYS_ASSERT(btr->search(t, ctr_key, v_ctr));
            ALWAYS_ASSERT(size_t(v_ctr) > 1);
            if (btr->search(t, i, v)) {
              ALWAYS_ASSERT(v = (txn_btree::value_type) i);
              btr->remove(t, i);
              v_ctr = (txn_btree::value_type)(size_t(v_ctr) - 1);
            } else {
              btr->insert(t, i, (txn_btree::value_type) i);
              v_ctr = (txn_btree::value_type)(size_t(v_ctr) + 1);
            }
            btr->insert(t, ctr_key, v_ctr);
            t.commit();
          } catch (transaction_abort_exception &e) {
            goto retry;
          }
        }
      }
    }
  };

  class reader_worker : public txn_btree_worker, public txn_btree::search_range_callback {
  public:
    reader_worker(txn_btree &btr) : txn_btree_worker(btr), validations(0), ctr(0) {}
    virtual bool invoke(txn_btree::key_type k, txn_btree::value_type v)
    {
      ctr++;
      return true;
    }
    virtual void run()
    {
      while (running) {
        try {
          transaction t;
          txn_btree::value_type v_ctr = 0;
          ALWAYS_ASSERT(btr->search(t, ctr_key, v_ctr));
          ctr = 0;
          btr->search_range_call(t, range_begin, &range_end, *this);
          t.commit();
          ALWAYS_ASSERT(ctr == size_t(v_ctr));
          validations++;
        } catch (transaction_abort_exception &e) {

        }
      }
    }
    size_t validations;
  private:
    size_t ctr;
  };
}

static void
mp_test2()
{
  using namespace mp_test2_ns;

  txn_btree btr;
  {
    transaction t;
    size_t n = 0;
    for (size_t i = range_begin; i < range_end; i++)
      if ((i % 2) == 0) {
        btr.insert(t, i, (txn_btree::value_type) i);
        n++;
      }
    btr.insert(t, ctr_key, (txn_btree::value_type) n);
    t.commit();
  }

  mutate_worker w0(btr);
  reader_worker w1(btr);

  running = true;
  w0.start(); w1.start();
  sleep(10);
  running = false;
  w0.join(); w1.join();

  cerr << "validations: " << w1.validations << endl;
}

void
txn_btree::Test()
{
  test1();
  test2();
  mp_test1();
  mp_test2();
}
<commit_msg>abort stats<commit_after>#include <unistd.h>

#include "txn_btree.h"
#include "thread.h"
#include "util.h"

#define VERBOSE(expr) ((void)0)

using namespace std;
using namespace util;

bool
txn_btree::search(transaction &t, key_type k, value_type &v)
{
  assert(!t.btree || t.btree == this);
  t.btree = this;

  // priority is
  // 1) write set
  // 2) local read set
  // 3) range set
  // 4) query underlying tree
  //
  // note (1)-(3) are served by transaction::local_search()

  if (t.local_search(k, v))
    return (bool) v;

  btree::value_type underlying_v;
  if (!underlying_btree.search(k, underlying_v)) {
    // all records exist in the system at MIN_TID with no value
    transaction::read_record_t *read_rec = &t.read_set[k];
    read_rec->t = transaction::MIN_TID;
    read_rec->r = NULL;
    read_rec->ln = NULL;
    return false;
  } else {
    transaction::logical_node *ln = (transaction::logical_node *) underlying_v;
    assert(ln);
    transaction::tid_t start_t;
    transaction::record_t r;
    if (unlikely(!ln->stable_read(t.snapshot_tid, start_t, r))) {
      t.abort();
      throw transaction_abort_exception();
    }
    transaction::read_record_t *read_rec = &t.read_set[k];
    read_rec->t = start_t;
    read_rec->r = r;
    read_rec->ln = ln;
    v = read_rec->r;
    return read_rec->r;
  }
}

bool
txn_btree::txn_search_range_callback::invoke(key_type k, value_type v)
{
  VERBOSE(cerr << "search range k: " << k << endl);
  transaction::key_range_t r(invoked ? prev_key : lower, k);
  if (!r.is_empty_range())
    t->add_absent_range(r);
  prev_key = k;
  invoked = true;
  value_type local_v = 0;
  bool local_read = t->local_search(k, local_v);
  bool ret = true;
  if (local_read && local_v)
    ret = caller_callback->invoke(k, local_v);
  map<key_type, transaction::read_record_t>::const_iterator it =
    t->read_set.find(k);
  if (it == t->read_set.end()) {
    transaction::logical_node *ln = (transaction::logical_node *) v;
    assert(ln);
    transaction::tid_t start_t;
    transaction::record_t r;
    if (unlikely(!ln->stable_read(t->snapshot_tid, start_t, r))) {
      t->abort();
      throw transaction_abort_exception();
    }
    transaction::read_record_t *read_rec = &t->read_set[k];
    read_rec->t = start_t;
    read_rec->r = r;
    read_rec->ln = ln;
    VERBOSE(cerr << "read <t=" << start_t << ", r=" << size_t(r) << "> (local_read=" << local_read << ")" << endl);
    if (!local_read && r)
      ret = caller_callback->invoke(k, r);
  }
  if (ret)
    caller_stopped = true;
  return ret;
}

bool
txn_btree::absent_range_validation_callback::invoke(key_type k, value_type v)
{
  transaction::logical_node *ln = (transaction::logical_node *) v;
  assert(ln);
  //cout << "absent_range_validation_callback: key " << k << " found ln " << intptr_t(ln) << endl;
  bool did_write = t->write_set.find(k) != t->write_set.end();
  failed_flag = did_write ? !ln->is_latest_version(0) : !ln->stable_is_latest_version(0);
  return !failed_flag;
}

void
txn_btree::search_range_call(transaction &t,
                             key_type lower,
                             const key_type *upper,
                             search_range_callback &callback)
{
  assert(!t.btree || t.btree == this);
  t.btree = this;

  // many cases to consider:
  // 1) for each logical_node returned from the scan, we need to
  //    record it in our local read set. there are several cases:
  //    A) if the logical_node corresponds to a key we have written, then
  //       we emit the version from the local write set
  //    B) if the logical_node corresponds to a key we have previous read,
  //       then we emit the previous version
  // 2) for each logical_node node *not* returned from the scan, we need
  //    to record its absense. we optimize this by recording the absense
  //    of contiguous ranges
  if (upper && *upper <= lower)
    return;
  txn_search_range_callback c(&t, lower, upper, &callback);
  underlying_btree.search_range_call(lower, upper, c);
  if (c.caller_stopped)
    return;
  if (c.invoked && c.prev_key == (key_type)-1)
    return;
  if (upper)
    t.add_absent_range(transaction::key_range_t(c.invoked ? (c.prev_key + 1): lower, *upper));
  else
    t.add_absent_range(transaction::key_range_t(c.invoked ? c.prev_key : lower));
}

void
txn_btree::insert_impl(transaction &t, key_type k, value_type v)
{
  assert(!t.btree || t.btree == this);
  t.btree = this;
  t.write_set[k] = v;
}

struct test_callback_ctr {
  test_callback_ctr(size_t *ctr) : ctr(ctr) {}

  inline bool
  operator()(txn_btree::key_type k, txn_btree::value_type v) const
  {
    (*ctr)++;
    return true;
  }
  size_t *ctr;
};

static void
test1()
{
  txn_btree btr;

  struct rec { uint64_t v; };
  rec recs[10];
  for (size_t i = 0; i < ARRAY_NELEMS(recs); i++)
    recs[i].v = 0;

  {
    transaction t;
    txn_btree::value_type v;
    ALWAYS_ASSERT(!btr.search(t, 0, v));
    btr.insert(t, 0, (txn_btree::value_type) &recs[0]);
    ALWAYS_ASSERT(btr.search(t, 0, v));
    ALWAYS_ASSERT(v == (txn_btree::value_type) &recs[0]);
    t.commit();
    VERBOSE(cout << "------" << endl);
  }

  {
    transaction t0, t1;
    txn_btree::value_type v0, v1;

    ALWAYS_ASSERT(btr.search(t0, 0, v0));
    ALWAYS_ASSERT(v0 == (txn_btree::value_type) &recs[0]);

    btr.insert(t0, 0, (txn_btree::value_type) &recs[1]);

    ALWAYS_ASSERT(btr.search(t1, 0, v1));
    ALWAYS_ASSERT(v1 == (txn_btree::value_type) &recs[0]);

    t0.commit();
    t1.commit();
    VERBOSE(cout << "------" << endl);
  }

  {
    // racy insert
    transaction t0, t1;
    txn_btree::value_type v0, v1;

    ALWAYS_ASSERT(btr.search(t0, 0, v0));
    ALWAYS_ASSERT(v0 == (txn_btree::value_type) &recs[1]);
    btr.insert(t0, 0, (txn_btree::value_type) &recs[2]);

    ALWAYS_ASSERT(btr.search(t1, 0, v1));
    ALWAYS_ASSERT(v1 == (txn_btree::value_type) &recs[1]);
    btr.insert(t1, 0, (txn_btree::value_type) &recs[3]);

    t0.commit(); // succeeds

    try {
      t1.commit(); // fails
      ALWAYS_ASSERT(false);
    } catch (transaction_abort_exception &e) {

    }
    VERBOSE(cout << "------" << endl);
  }

  {
    // racy scan
    transaction t0, t1;

    txn_btree::key_type vend = 5;
    size_t ctr = 0;
    btr.search_range(t0, 1, &vend, test_callback_ctr(&ctr));
    ALWAYS_ASSERT(ctr == 0);

    btr.insert(t1, 2, (txn_btree::value_type) &recs[4]);
    t1.commit();

    btr.insert(t0, 0, (txn_btree::value_type) &recs[0]);
    try {
      t0.commit(); // fails
      ALWAYS_ASSERT(false);
    } catch (transaction_abort_exception &e) {

    }
    VERBOSE(cout << "------" << endl);
  }

  {
    transaction t;
    txn_btree::key_type vend = 20;
    size_t ctr = 0;
    btr.search_range(t, 10, &vend, test_callback_ctr(&ctr));
    ALWAYS_ASSERT(ctr == 0);
    btr.insert(t, 15, (txn_btree::value_type) &recs[5]);
    t.commit();
    VERBOSE(cout << "------" << endl);
  }
}

static void
test2()
{
  txn_btree btr;
  for (size_t i = 0; i < 100; i++) {
    transaction t;
    btr.insert(t, 0, (txn_btree::value_type) 123);
    t.commit();
  }
}

class txn_btree_worker : public ndb_thread {
public:
  txn_btree_worker(txn_btree &btr) : btr(&btr) {}
protected:
  txn_btree *btr;
};

namespace mp_test1_ns {
  // read-modify-write test (counters)

  struct record {
    record() : v(0) {}
    uint64_t v;
  };

  const size_t niters = 1000;

  class worker : public txn_btree_worker {
  public:
    worker(txn_btree &btr) : txn_btree_worker(btr) {}
    ~worker()
    {
      for (vector<record *>::iterator it = recs.begin();
           it != recs.end(); ++it)
        delete *it;
    }
    virtual void run()
    {
      for (size_t i = 0; i < niters; i++) {
      retry:
        transaction t;
        record *rec = new record;
        recs.push_back(rec);
        try {
          txn_btree::value_type v = 0;
          if (!btr->search(t, 0, v)) {
            rec->v = 1;
          } else {
            *rec = *((record *)v);
            rec->v++;
          }
          btr->insert(t, 0, (txn_btree::value_type) rec);
          t.commit();
        } catch (transaction_abort_exception &e) {
          goto retry;
        }
      }
    }
  private:
    vector<record *> recs;
  };

}

static void
mp_test1()
{
  using namespace mp_test1_ns;

  txn_btree btr;

  worker w0(btr);
  worker w1(btr);

  w0.start(); w1.start();
  w0.join(); w1.join();

  transaction t;
  txn_btree::value_type v = 0;
  ALWAYS_ASSERT(btr.search(t, 0, v));
  ALWAYS_ASSERT( ((record *) v)->v == (niters * 2) );
  t.commit();

}

namespace mp_test2_ns {

  static const txn_btree::key_type ctr_key     = 0;
  static const txn_btree::key_type range_begin = 100;
  static const txn_btree::key_type range_end   = 200;

  static volatile bool running = true;

  class mutate_worker : public txn_btree_worker {
  public:
    mutate_worker(txn_btree &btr) : txn_btree_worker(btr), naborts(0) {}
    virtual void run()
    {
      while (running) {
        for (size_t i = range_begin; running && i < range_end; i++) {
        retry:
          transaction t;
          try {
            txn_btree::value_type v = 0, v_ctr = 0;
            ALWAYS_ASSERT(btr->search(t, ctr_key, v_ctr));
            ALWAYS_ASSERT(size_t(v_ctr) > 1);
            if (btr->search(t, i, v)) {
              ALWAYS_ASSERT(v = (txn_btree::value_type) i);
              btr->remove(t, i);
              v_ctr = (txn_btree::value_type)(size_t(v_ctr) - 1);
            } else {
              btr->insert(t, i, (txn_btree::value_type) i);
              v_ctr = (txn_btree::value_type)(size_t(v_ctr) + 1);
            }
            btr->insert(t, ctr_key, v_ctr);
            t.commit();
          } catch (transaction_abort_exception &e) {
            naborts++;
            goto retry;
          }
        }
      }
    }
    size_t naborts;
  };

  class reader_worker : public txn_btree_worker, public txn_btree::search_range_callback {
  public:
    reader_worker(txn_btree &btr) : txn_btree_worker(btr), validations(0), naborts(0), ctr(0) {}
    virtual bool invoke(txn_btree::key_type k, txn_btree::value_type v)
    {
      ctr++;
      return true;
    }
    virtual void run()
    {
      while (running) {
        try {
          transaction t;
          txn_btree::value_type v_ctr = 0;
          ALWAYS_ASSERT(btr->search(t, ctr_key, v_ctr));
          ctr = 0;
          btr->search_range_call(t, range_begin, &range_end, *this);
          t.commit();
          ALWAYS_ASSERT(ctr == size_t(v_ctr));
          validations++;
        } catch (transaction_abort_exception &e) {
          naborts++;
        }
      }
    }
    size_t validations;
    size_t naborts;
  private:
    size_t ctr;
  };
}

static void
mp_test2()
{
  using namespace mp_test2_ns;

  txn_btree btr;
  {
    transaction t;
    size_t n = 0;
    for (size_t i = range_begin; i < range_end; i++)
      if ((i % 2) == 0) {
        btr.insert(t, i, (txn_btree::value_type) i);
        n++;
      }
    btr.insert(t, ctr_key, (txn_btree::value_type) n);
    t.commit();
  }

  mutate_worker w0(btr);
  reader_worker w1(btr);

  running = true;
  w0.start(); w1.start();
  sleep(10);
  running = false;
  w0.join(); w1.join();

  cerr << "mutate naborts: " << w0.naborts << endl;
  cerr << "reader naborts: " << w1.naborts << endl;
  cerr << "validations: " << w1.validations << endl;
}

void
txn_btree::Test()
{
  test1();
  test2();
  mp_test1();
  mp_test2();
}
<|endoftext|>


<commit_before>#include <ros/ros.h>
#include <tf2_ros/transform_broadcaster.h>

#include <geometry_msgs/PointStamped.h>
#include <geometry_msgs/TransformStamped.h>

class TomatoBroadcaster {
  tf2_ros::TransformBroadcaster broadcaster_;
  geometry_msgs::TransformStamped transform_;
  ros::Subscriber sub_;

public:
  TomatoBroadcaster(ros::NodeHandle node_handle)
    : transform_ {},
      sub_ {node_handle.subscribe<geometry_msgs::PointStamped>("tomato_point", 1, &TomatoBroadcaster::callback, this)}
  {
  }

private:
  void callback(const geometry_msgs::PointStamped::ConstPtr& msg)
  {
    transform_.header = msg->header;
    transform_.header.frame_id = "kinect";
    transform_.child_frame_id = "tomato";

    transform_.transform.translation.x = msg->point.x;
    transform_.transform.translation.y = msg->point.y;
    transform_.transform.translation.z = msg->point.z;
    transform_.transform.rotation.w = 1.0;

    broadcaster_.sendTransform(transform_);
  }
};

int main(int argc, char** argv){
  ros::init(argc, argv, "kinect_tomato_broadcaster_node");
  ros::NodeHandle node_handle;

  TomatoBroadcaster broadcaster {node_handle};

  ros::spin();

  return 0;
}
<commit_msg>Update message type for PoseStamped<commit_after>#include <ros/ros.h>
#include <tf2_ros/transform_broadcaster.h>

#include <geometry_msgs/PoseStamped.h>
#include <geometry_msgs/TransformStamped.h>

class TomatoBroadcaster {
  tf2_ros::TransformBroadcaster broadcaster_;
  geometry_msgs::TransformStamped transform_;
  ros::Subscriber sub_;

public:
  TomatoBroadcaster(ros::NodeHandle& node_handle)
    : broadcaster_ {},
      transform_ {},
      sub_ {node_handle.subscribe<geometry_msgs::PoseStamped>("tomato_point", 1, &TomatoBroadcaster::callback, this)}
  {
  }

private:
  void callback(const geometry_msgs::PoseStamped::ConstPtr& msg)
  {
    transform_.header = msg->header;
    transform_.header.frame_id = "kinect";
    transform_.child_frame_id = "tomato";

    transform_.transform.translation.x = msg->pose.position.x;
    transform_.transform.translation.y = msg->pose.position.y;
    transform_.transform.translation.z = msg->pose.position.z;
    transform_.transform.rotation = msg->pose.orientation;

    broadcaster_.sendTransform(transform_);
  }
};

int main(int argc, char** argv){
  ros::init(argc, argv, "kinect_tomato_broadcaster_node");
  ros::NodeHandle node_handle;

  TomatoBroadcaster broadcaster {node_handle};

  ros::spin();

  return 0;
}
<|endoftext|>


<commit_before>#include <algorithm>
#include <fstream>
#include <iostream>
#include <map>
#include <sstream>
#include <string>
#include <vector>

#include <getopt.h>

#include "persistenceDiagrams/IO.hh"
#include "persistenceDiagrams/PersistenceDiagram.hh"
#include "persistenceDiagrams/PersistenceIndicatorFunction.hh"

using DataType           = double;
using PersistenceDiagram = aleph::PersistenceDiagram<DataType>;
using StepFunction       = aleph::math::StepFunction<double>;

/*
  Prints a vector in a simple matrix-like format. Given a row index, all
  of the entries are considered to be the columns of the matrix:

    Input:   {4,5,6}, row = 23

    Output:  23 0 4
             23 1 5
             23 2 6
             <empty line>

  This output format is flexible and permits direct usage in other tools
  such as TikZ or pgfplots.
*/

template <class Map> void print( std::ostream& o, const Map& m, unsigned row )
{
  unsigned column = 0;
  for( auto it = m.begin(); it != m.end(); ++it )
    o << column++ << "\t" << row << "\t" << *it << "\n";

  o << "\n";
}

int main( int argc, char** argv )
{
  static option commandLineOptions[] =
  {
    { "min-k", required_argument, nullptr, 'k' },
    { "max-k", required_argument, nullptr, 'K' },
    { nullptr, 0                , nullptr,  0  }
  };

  int option = 0;

  unsigned minK = 0;
  unsigned maxK = 0;

  while( ( option = getopt_long( argc, argv, "k:K:", commandLineOptions, nullptr ) ) != -1 )
  {
    switch( option )
    {
    case 'k':
      minK = unsigned( std::stoul( optarg ) );
      break;
    case 'K':
      maxK = unsigned( std::stoul( optarg ) );
      break;
    default:
      break;
    }
  }

  if( argc - optind < 2 )
  {
    // TODO: Show usage
    return -1;
  }

  unsigned n = static_cast<unsigned>( std::stoul( argv[optind++] ) );

  std::vector<std::string> filenames;
  std::vector<PersistenceDiagram> persistenceDiagrams;
  std::vector<StepFunction> persistenceIndicatorFunctions;

  std::set<DataType> domain;

  for( int i = optind; i < argc; i++ )
  {
    filenames.push_back( argv[i] );

    std::cerr << "* Processing '" << argv[i] << "'...";

    PersistenceDiagram persistenceDiagram
        = aleph::load<DataType>( filenames.back() );

    persistenceDiagram.removeDiagonal();
    persistenceDiagram.removeUnpaired();

    persistenceDiagrams.push_back( persistenceDiagram );
    persistenceIndicatorFunctions.push_back( aleph::persistenceIndicatorFunction( persistenceDiagram ) );

    persistenceIndicatorFunctions.back().domain(
      std::inserter( domain, domain.begin() ) );

    std::cerr << "finished\n";
  }

  if( domain.empty() )
    return -1;

  auto min = *domain.begin();
  auto max = *domain.rbegin();

  std::cerr << "* Domain: [" << min << "," << max << "]\n";

  // Prepare bins ------------------------------------------------------

  std::vector<DataType> linbins;
  std::vector<DataType> logbins;

  linbins.reserve( n );
  logbins.reserve( n );

  for( unsigned i = 0; i < n; i++ )
  {
    auto offset = ( max - min ) / (n-1);
    auto value  = min + i * offset;

    linbins.push_back( value );
  }

  std::cerr << "* Linear-spaced bins: ";

  for( auto&& linbin : linbins )
    std::cerr << linbin << " ";

  std::cerr << "\n";

  for( unsigned i = 0; i < n; i++ )
  {
    auto offset = ( std::log10( max ) - std::log10( min ) ) / (n-1);
    auto value  = std::log10( min ) + i * offset;

    logbins.push_back( value );
  }

  std::transform( logbins.begin(), logbins.end(),
                  logbins.begin(),
                  [] ( const DataType x )
                  {
                    return std::pow( DataType(10), x );
                  } );

  std::cerr << "* Log-spaced bins: ";

  for( auto&& logbin : logbins )
    std::cerr << logbin << " ";

  std::cerr << "\n";

  for( auto it = linbins.begin(); it != linbins.end(); ++it )
  {
    if( it != linbins.begin() )
    {
      auto prev = std::prev( it );
      domain.insert( ( *prev + *it ) / 2.0 );
    }
  }

  for( auto it = logbins.begin(); it != logbins.end(); ++it )
  {
    if( it != logbins.begin() && std::isfinite( *it ) )
    {
      auto prev = std::prev( it );
      domain.insert( ( *prev + *it ) / 2.0 );
    }
  }

  // Prepare histogram calculation -------------------------------------

  auto valueToLinIndex = [&min, &max, &linbins, &n] ( DataType value )
  {
    auto offset = ( max - min ) / (n-1);
    return static_cast<std::size_t>( ( value - min ) / offset );
  };

  auto valueToLogIndex = [&min, &max, &logbins, &n] ( DataType value )
  {
    auto offset = ( std::log10( max ) - std::log10( min ) ) / (n-1);
    return static_cast<std::size_t>( ( std::log10( value ) - std::log10( min ) ) / offset );
  };

  std::ofstream linout( "/tmp/DNA_" + std::to_string( n ) + "_lin.txt" );
  std::ofstream logout( "/tmp/DNA_" + std::to_string( n ) + "_log.txt" );

  unsigned index = 0;
  for( auto&& pif : persistenceIndicatorFunctions )
  {
    std::vector<DataType> linhist(n);
    std::vector<DataType> loghist(n);

    for( auto&& x : domain )
    {
      auto value  = pif(x);
      auto linbin = valueToLinIndex(x);
      auto logbin = valueToLogIndex(x);

      linhist.at(linbin) = std::max( linhist.at(linbin), value );

      if( logbin < loghist.size() )
        loghist.at(logbin) = std::max( loghist.at(logbin), value );
    }

    print( linout, linhist, index );
    print( logout, loghist, index );

    ++index;
  }

  // Extend the output with sufficiently many empty histograms. This
  // ensures that the output has the same dimensions.
  for( unsigned i = unsigned( persistenceIndicatorFunctions.size() ); i < maxK; i++ )
  {
    std::vector<DataType> hist(n);

    print( linout, hist, i );
    print( logout, hist, i );
  }
}
<commit_msg>Fixed bin creation for histograms<commit_after>#include <algorithm>
#include <fstream>
#include <iostream>
#include <map>
#include <sstream>
#include <string>
#include <vector>

#include <cmath>

#include <getopt.h>

#include "persistenceDiagrams/IO.hh"
#include "persistenceDiagrams/PersistenceDiagram.hh"
#include "persistenceDiagrams/PersistenceIndicatorFunction.hh"

using DataType           = double;
using PersistenceDiagram = aleph::PersistenceDiagram<DataType>;
using StepFunction       = aleph::math::StepFunction<double>;

/*
  Prints a vector in a simple matrix-like format. Given a row index, all
  of the entries are considered to be the columns of the matrix:

    Input:   {4,5,6}, row = 23

    Output:  23 0 4
             23 1 5
             23 2 6
             <empty line>

  This output format is flexible and permits direct usage in other tools
  such as TikZ or pgfplots.
*/

template <class Map> void print( std::ostream& o, const Map& m, unsigned row )
{
  unsigned column = 0;
  for( auto it = m.begin(); it != m.end(); ++it )
    o << column++ << "\t" << row << "\t" << *it << "\n";

  o << "\n";
}

int main( int argc, char** argv )
{
  static option commandLineOptions[] =
  {
    { "min-k", required_argument, nullptr, 'k' },
    { "max-k", required_argument, nullptr, 'K' },
    { nullptr, 0                , nullptr,  0  }
  };

  int option = 0;

  unsigned minK = 0;
  unsigned maxK = 0;

  while( ( option = getopt_long( argc, argv, "k:K:", commandLineOptions, nullptr ) ) != -1 )
  {
    switch( option )
    {
    case 'k':
      minK = unsigned( std::stoul( optarg ) );
      break;
    case 'K':
      maxK = unsigned( std::stoul( optarg ) );
      break;
    default:
      break;
    }
  }

  if( argc - optind < 2 )
  {
    // TODO: Show usage
    return -1;
  }

  unsigned n = static_cast<unsigned>( std::stoul( argv[optind++] ) );

  std::vector<std::string> filenames;
  std::vector<PersistenceDiagram> persistenceDiagrams;
  std::vector<StepFunction> persistenceIndicatorFunctions;

  std::set<DataType> domain;

  for( int i = optind; i < argc; i++ )
  {
    filenames.push_back( argv[i] );

    std::cerr << "* Processing '" << argv[i] << "'...";

    PersistenceDiagram persistenceDiagram
        = aleph::load<DataType>( filenames.back() );

    persistenceDiagram.removeDiagonal();
    persistenceDiagram.removeUnpaired();

    persistenceDiagrams.push_back( persistenceDiagram );
    persistenceIndicatorFunctions.push_back( aleph::persistenceIndicatorFunction( persistenceDiagram ) );

    persistenceIndicatorFunctions.back().domain(
      std::inserter( domain, domain.begin() ) );

    std::cerr << "finished\n";
  }

  if( domain.empty() )
    return -1;

  auto min = *domain.begin();
  auto max = std::nextafter( *domain.rbegin(), std::numeric_limits<DataType>::max() );

  std::cerr << "* Domain: [" << min << "," << max << "]\n";

  // Prepare bins ------------------------------------------------------

  std::vector<DataType> linbins;
  std::vector<DataType> logbins;

  linbins.reserve( n );
  logbins.reserve( n );

  for( unsigned i = 0; i < n; i++ )
  {
    auto offset = ( max - min ) / n;
    auto value  = min + i * offset;

    linbins.push_back( value );
  }

  std::cerr << "* Linear-spaced bins: ";

  for( auto&& linbin : linbins )
    std::cerr << linbin << " ";

  std::cerr << "\n";

  for( unsigned i = 0; i < n; i++ )
  {
    auto offset = ( std::log10( max ) - std::log10( min ) ) / (n-1);
    auto value  = std::log10( min ) + i * offset;

    logbins.push_back( value );
  }

  std::transform( logbins.begin(), logbins.end(),
                  logbins.begin(),
                  [] ( const DataType x )
                  {
                    return std::pow( DataType(10), x );
                  } );

  std::cerr << "* Log-spaced bins: ";

  for( auto&& logbin : logbins )
    std::cerr << logbin << " ";

  std::cerr << "\n";

  for( auto it = linbins.begin(); it != linbins.end(); ++it )
  {
    if( it != linbins.begin() )
    {
      auto prev = std::prev( it );
      domain.insert( ( *prev + *it ) / 2.0 );
    }
  }

  for( auto it = logbins.begin(); it != logbins.end(); ++it )
  {
    if( it != logbins.begin() && std::isfinite( *it ) )
    {
      auto prev = std::prev( it );
      domain.insert( ( *prev + *it ) / 2.0 );
    }
  }

  // Prepare histogram calculation -------------------------------------

  auto valueToLinIndex = [&min, &max, &linbins, &n] ( DataType value )
  {
    auto offset = ( max - min ) / n;
    return static_cast<std::size_t>( ( value - min ) / offset );
  };

  auto valueToLogIndex = [&min, &max, &logbins, &n] ( DataType value )
  {
    auto offset = ( std::log10( max ) - std::log10( min ) ) / n;
    return static_cast<std::size_t>( ( std::log10( value ) - std::log10( min ) ) / offset );
  };

  std::ofstream linout( "/tmp/DNA_" + std::to_string( n ) + "_lin.txt" );
  std::ofstream logout( "/tmp/DNA_" + std::to_string( n ) + "_log.txt" );

  unsigned index = 0;
  for( auto&& pif : persistenceIndicatorFunctions )
  {
    std::vector<DataType> linhist(n);
    std::vector<DataType> loghist(n);

    for( auto&& x : domain )
    {
      auto value  = pif(x);
      auto linbin = valueToLinIndex(x);
      auto logbin = valueToLogIndex(x);

      linhist.at(linbin) = std::max( linhist.at(linbin), value );

      if( logbin < loghist.size() )
        loghist.at(logbin) = std::max( loghist.at(logbin), value );
    }

    print( linout, linhist, index );
    print( logout, loghist, index );

    ++index;
  }

  // Extend the output with sufficiently many empty histograms. This
  // ensures that the output has the same dimensions.
  for( unsigned i = unsigned( persistenceIndicatorFunctions.size() ); i < maxK; i++ )
  {
    std::vector<DataType> hist(n);

    print( linout, hist, i );
    print( logout, hist, i );
  }
}
<|endoftext|>


<commit_before>// Copyright 2014 MongoDB Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "catch.hpp"
#include "helpers.hpp"

#include <mongocxx/private/libmongoc.hpp>

#include <mongocxx/client.hpp>
#include <mongocxx/database.hpp>

using namespace mongocxx;

TEST_CASE("A database", "[database]") {
    const std::string database_name("database");
    MOCK_CLIENT
    MOCK_DATABASE
    client mongo_client;

    SECTION("is created by a client") {
        bool called = false;
        get_database->interpose([&](mongoc_client_t* client, const char* c_name) {
            called = true;
            REQUIRE(database_name == c_name);
            return nullptr;
        });

        database obtained_database = mongo_client[database_name];
        REQUIRE(called);
        REQUIRE(obtained_database.name() == database_name);
    }
    SECTION("cleans up its underlying mongoc database on destruction") {
        bool destroy_called = false;

        database_destroy->interpose([&](mongoc_database_t* client) { destroy_called = true; });

        {
            database database = mongo_client["database"];
            REQUIRE(!destroy_called);
        }

        REQUIRE(destroy_called);
    }

    SECTION("supports move operations") {
        bool destroy_called = false;
        database_destroy->interpose([&](mongoc_database_t* client) { destroy_called = true; });

        {
            client mongo_client;
            database a = mongo_client[database_name];

            database b{std::move(a)};
            REQUIRE(!destroy_called);

            database c = std::move(b);
            REQUIRE(!destroy_called);
        }
        REQUIRE(destroy_called);
    }

    SECTION("has a read preferences which may be set and obtained") {
        bool destroy_called = false;
        database_destroy->interpose([&](mongoc_database_t* client) { destroy_called = true; });

        database mongo_database(mongo_client["database"]);
        read_preference preference{read_preference::read_mode::k_secondary_preferred};

        auto deleter = [](mongoc_read_prefs_t* var) { mongoc_read_prefs_destroy(var); };
        std::unique_ptr<mongoc_read_prefs_t, decltype(deleter)> saved_preference(nullptr, deleter);

        bool called = false;
        database_set_preference->interpose([&](mongoc_database_t* db,
                                               const mongoc_read_prefs_t* read_prefs) {
            called = true;
            saved_preference.reset(mongoc_read_prefs_copy(read_prefs));
            REQUIRE(
                mongoc_read_prefs_get_mode(read_prefs) ==
                static_cast<mongoc_read_mode_t>(read_preference::read_mode::k_secondary_preferred));
        });

        database_get_preference->interpose([&](const mongoc_database_t* client) {
                                               return saved_preference.get();
                                           }).forever();

        mongo_database.read_preference(std::move(preference));
        REQUIRE(called);

        REQUIRE(read_preference::read_mode::k_secondary_preferred ==
                mongo_database.read_preference().mode());
    }

    SECTION("has a write concern which may be set and obtained") {
        bool destroy_called = false;
        database_destroy->interpose([&](mongoc_database_t* client) { destroy_called = true; });

        database mongo_database(mongo_client[database_name]);
        write_concern concern;
        concern.majority(std::chrono::milliseconds(100));

        mongoc_write_concern_t* underlying_wc;

        bool set_called = false;
        database_set_concern->interpose(
            [&](mongoc_database_t* client, const mongoc_write_concern_t* concern) {
                set_called = true;
                underlying_wc = mongoc_write_concern_copy(concern);
            });

        bool get_called = false;
        database_get_concern->interpose([&](const mongoc_database_t* client) {
            get_called = true;
            return underlying_wc;
        });

        mongo_database.write_concern(concern);
        REQUIRE(set_called);

        MOCK_CONCERN
        bool copy_called = false;
        concern_copy->interpose([&](const mongoc_write_concern_t* concern) {
            copy_called = true;
            return mongoc_write_concern_copy(underlying_wc);
        });

        REQUIRE(concern.majority() == mongo_database.write_concern().majority());

        REQUIRE(get_called);
        REQUIRE(copy_called);
    }

    SECTION("may create a collection") {
        MOCK_COLLECTION
        const std::string collection_name("collection");
        database database = mongo_client[database_name];
        collection obtained_collection = database[collection_name];
        REQUIRE(obtained_collection.name() == collection_name);
    }
}
<commit_msg>Fix some names and whitespace<commit_after>// Copyright 2014 MongoDB Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "catch.hpp"
#include "helpers.hpp"

#include <mongocxx/private/libmongoc.hpp>

#include <mongocxx/client.hpp>
#include <mongocxx/database.hpp>

using namespace mongocxx;

TEST_CASE("A database", "[database]") {
    const std::string database_name("database");
    MOCK_CLIENT
    MOCK_DATABASE
    client mongo_client;

    SECTION("is created by a client") {
        bool called = false;
        get_database->interpose([&](mongoc_client_t* client, const char* d_name) {
            called = true;
            REQUIRE(database_name == d_name);
            return nullptr;
        });

        database obtained_database = mongo_client[database_name];
        REQUIRE(called);
        REQUIRE(obtained_database.name() == database_name);
    }

    SECTION("cleans up its underlying mongoc database on destruction") {
        bool destroy_called = false;

        database_destroy->interpose([&](mongoc_database_t* client) { destroy_called = true; });

        {
            database database = mongo_client["database"];
            REQUIRE(!destroy_called);
        }

        REQUIRE(destroy_called);
    }

    SECTION("supports move operations") {
        bool destroy_called = false;
        database_destroy->interpose([&](mongoc_database_t* client) { destroy_called = true; });

        {
            client mongo_client;
            database a = mongo_client[database_name];

            database b{std::move(a)};
            REQUIRE(!destroy_called);

            database c = std::move(b);
            REQUIRE(!destroy_called);
        }
        REQUIRE(destroy_called);
    }

    SECTION("has a read preferences which may be set and obtained") {
        bool destroy_called = false;
        database_destroy->interpose([&](mongoc_database_t* client) { destroy_called = true; });

        database mongo_database(mongo_client["database"]);
        read_preference preference{read_preference::read_mode::k_secondary_preferred};

        auto deleter = [](mongoc_read_prefs_t* var) { mongoc_read_prefs_destroy(var); };
        std::unique_ptr<mongoc_read_prefs_t, decltype(deleter)> saved_preference(nullptr, deleter);

        bool called = false;
        database_set_preference->interpose([&](mongoc_database_t* db,
                                               const mongoc_read_prefs_t* read_prefs) {
            called = true;
            saved_preference.reset(mongoc_read_prefs_copy(read_prefs));
            REQUIRE(
                mongoc_read_prefs_get_mode(read_prefs) ==
                static_cast<mongoc_read_mode_t>(read_preference::read_mode::k_secondary_preferred));
        });

        database_get_preference->interpose([&](const mongoc_database_t* client) {
                                               return saved_preference.get();
                                           }).forever();

        mongo_database.read_preference(std::move(preference));
        REQUIRE(called);

        REQUIRE(read_preference::read_mode::k_secondary_preferred ==
                mongo_database.read_preference().mode());
    }

    SECTION("has a write concern which may be set and obtained") {
        bool destroy_called = false;
        database_destroy->interpose([&](mongoc_database_t* client) { destroy_called = true; });

        database mongo_database(mongo_client[database_name]);
        write_concern concern;
        concern.majority(std::chrono::milliseconds(100));

        mongoc_write_concern_t* underlying_wc;

        bool set_called = false;
        database_set_concern->interpose(
            [&](mongoc_database_t* client, const mongoc_write_concern_t* concern) {
                set_called = true;
                underlying_wc = mongoc_write_concern_copy(concern);
            });

        bool get_called = false;
        database_get_concern->interpose([&](const mongoc_database_t* client) {
            get_called = true;
            return underlying_wc;
        });

        mongo_database.write_concern(concern);
        REQUIRE(set_called);

        MOCK_CONCERN
        bool copy_called = false;
        concern_copy->interpose([&](const mongoc_write_concern_t* concern) {
            copy_called = true;
            return mongoc_write_concern_copy(underlying_wc);
        });

        REQUIRE(concern.majority() == mongo_database.write_concern().majority());

        REQUIRE(get_called);
        REQUIRE(copy_called);
    }

    SECTION("may create a collection") {
        MOCK_COLLECTION
        const std::string collection_name("collection");
        database database = mongo_client[database_name];
        collection obtained_collection = database[collection_name];
        REQUIRE(obtained_collection.name() == collection_name);
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright 2002-2013 Jose Fonseca
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <map>

#include <assert.h>
#include <stdlib.h>

#include <windows.h>
#include <tchar.h>
#include <ntstatus.h>

#include "debugger.h"
#include "log.h"
#include "outdbg.h"
#include "symbols.h"

typedef struct {
    HANDLE hThread;
}
THREAD_INFO, * PTHREAD_INFO;

typedef std::map< DWORD, THREAD_INFO > THREAD_INFO_LIST;

typedef struct {
    HANDLE hProcess;
    THREAD_INFO_LIST Threads;
}
PROCESS_INFO, * PPROCESS_INFO;

typedef std::map< DWORD, PROCESS_INFO> PROCESS_INFO_LIST;
static PROCESS_INFO_LIST g_Processes;

BOOL ObtainSeDebugPrivilege(void)
{
    HANDLE hToken;
    PTOKEN_PRIVILEGES NewPrivileges;
    BYTE OldPriv[1024];
    PBYTE pbOldPriv;
    ULONG cbNeeded;
    BOOLEAN fRc;
    LUID LuidPrivilege;

    // Make sure we have access to adjust and to get the old token privileges
    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
    {
        OutputDebug("OpenProcessToken failed with 0x%08lx\n", GetLastError());

        return FALSE;
    }

    cbNeeded = 0;

    // Initialize the privilege adjustment structure
    LookupPrivilegeValue( NULL, SE_DEBUG_NAME, &LuidPrivilege );

    NewPrivileges = (PTOKEN_PRIVILEGES) LocalAlloc(
        LMEM_ZEROINIT,
        sizeof(TOKEN_PRIVILEGES) + (1 - ANYSIZE_ARRAY)*sizeof(LUID_AND_ATTRIBUTES)
    );
    if(NewPrivileges == NULL) {
        return FALSE;
    }

    NewPrivileges->PrivilegeCount = 1;
    NewPrivileges->Privileges[0].Luid = LuidPrivilege;
    NewPrivileges->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

    // Enable the privilege
    pbOldPriv = OldPriv;
    fRc = AdjustTokenPrivileges(
        hToken,
        FALSE,
        NewPrivileges,
        1024,
        (PTOKEN_PRIVILEGES)pbOldPriv,
        &cbNeeded
    );

    if (!fRc) {

        // If the stack was too small to hold the privileges
        // then allocate off the heap
        if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {

            pbOldPriv = (PBYTE)LocalAlloc(LMEM_FIXED, cbNeeded);
            if (pbOldPriv == NULL) {
                return FALSE;
            }

            fRc = AdjustTokenPrivileges(
                hToken,
                FALSE,
                NewPrivileges,
                cbNeeded,
                (PTOKEN_PRIVILEGES)pbOldPriv,
                &cbNeeded
            );
        }
    }
    return fRc;
}

static BOOL CALLBACK
symCallback(HANDLE hProcess,
            ULONG ActionCode,
            ULONG64 CallbackData,
            ULONG64 UserContext)
{
    if (ActionCode == CBA_DEBUG_INFO) {
        lprintf("%s", (LPCSTR)(UINT_PTR)CallbackData);
        return TRUE;
    }

    return FALSE;
}

static char *
readProcessString(HANDLE hProcess, LPCVOID lpBaseAddress, SIZE_T nSize)
{
    LPSTR lpszBuffer = (LPSTR)malloc(nSize + 1);
    SIZE_T NumberOfBytesRead = 0;

    if (!ReadProcessMemory(hProcess, lpBaseAddress, lpszBuffer, nSize, &NumberOfBytesRead)) {
        lpszBuffer[0] = '\0';
    }

    assert(NumberOfBytesRead <= nSize);
    lpszBuffer[NumberOfBytesRead] = '\0';
    return lpszBuffer;
}

BOOL DebugMainLoop(const DebugOptions *pOptions)
{
    BOOL fFinished = FALSE;
    BOOL fBreakpointSignalled = FALSE;
    BOOL fWowBreakpointSignalled = FALSE;

    while(!fFinished)
    {
        DEBUG_EVENT DebugEvent;            // debugging event information
        DWORD dwContinueStatus = DBG_CONTINUE;    // exception continuation
        PPROCESS_INFO pProcessInfo;
        PTHREAD_INFO pThreadInfo;

        // Wait for a debugging event to occur. The second parameter indicates
        // that the function does not return until a debugging event occurs.
        if(!WaitForDebugEvent(&DebugEvent, INFINITE))
        {
            OutputDebug("WaitForDebugEvent: 0x%08lx", GetLastError());

            return FALSE;
        }

        // Process the debugging event code.
        switch (DebugEvent.dwDebugEventCode) {
        case EXCEPTION_DEBUG_EVENT: {
            PEXCEPTION_RECORD pExceptionRecord = &DebugEvent.u.Exception.ExceptionRecord;
            NTSTATUS ExceptionCode = pExceptionRecord->ExceptionCode;

            // Process the exception code. When handling
            // exceptions, remember to set the continuation
            // status parameter (dwContinueStatus). This value
            // is used by the ContinueDebugEvent function.
            if (pOptions->debug_flag) {
                lprintf("EXCEPTION PID=%lu TID=%lu ExceptionCode=0x%lx dwFirstChance=%lu\r\n",
                        DebugEvent.dwProcessId,
                        DebugEvent.dwThreadId,
                        pExceptionRecord->ExceptionCode,
                        DebugEvent.u.Exception.dwFirstChance
                );
            }

            dwContinueStatus = DBG_EXCEPTION_NOT_HANDLED;

            if (DebugEvent.u.Exception.dwFirstChance) {
                if (pExceptionRecord->ExceptionCode == (DWORD)STATUS_BREAKPOINT) {
                    // Signal the aedebug event
                    if (!fBreakpointSignalled) {
                        fBreakpointSignalled = TRUE;

                        if (pOptions->hEvent) {
                            SetEvent(pOptions->hEvent);
                            CloseHandle(pOptions->hEvent);
                        }

                        /*
                         * We ignore first-chance breakpoints by default.
                         *
                         * We get one of these whenever we attach to a process.
                         * But in some cases, we never get a second-chance, e.g.,
                         * when we're attached through MSVCRT's abort().
                         */
                        if (!pOptions->breakpoint_flag) {
                            dwContinueStatus = DBG_CONTINUE;
                            break;
                        }
                    }
                }

                if (ExceptionCode == STATUS_WX86_BREAKPOINT) {
                    if (!fWowBreakpointSignalled) {
                        fWowBreakpointSignalled = TRUE;
                        dwContinueStatus = DBG_CONTINUE;
                    }
                }

                if (!pOptions->first_chance) {
                    break;
                }
            }

            // Find the process in the process list
            pProcessInfo = &g_Processes[DebugEvent.dwProcessId];

            SymRefreshModuleList(pProcessInfo->hProcess);

            dumpException(pProcessInfo->hProcess,
                          &DebugEvent.u.Exception.ExceptionRecord);

            // Find the thread in the thread list
            THREAD_INFO_LIST::const_iterator it;
            for (it = pProcessInfo->Threads.begin(); it != pProcessInfo->Threads.end(); ++it) {
                DWORD dwThreadId = it->first;
                if (dwThreadId != DebugEvent.dwThreadId &&
                    ExceptionCode != STATUS_BREAKPOINT &&
                    !pOptions->verbose_flag) {
                        continue;
                }

                dumpStack(pProcessInfo->hProcess,
                          pThreadInfo->hThread, NULL);
            }

            if (!DebugEvent.u.Exception.dwFirstChance) {
                /*
                 * Terminate the process. As continuing would cause the JIT debugger
                 * to be invoked again.
                 */
                TerminateProcess(pProcessInfo->hProcess, (UINT)ExceptionCode);
            }

            break;
        }

        case CREATE_THREAD_DEBUG_EVENT:
            if (pOptions->debug_flag) {
                lprintf("CREATE_THREAD PID=%lu TID=%lu\r\n",
                        DebugEvent.dwProcessId,
                        DebugEvent.dwThreadId
                );
            }

            // Add the thread to the thread list
            pProcessInfo = &g_Processes[DebugEvent.dwProcessId];
            pThreadInfo = &pProcessInfo->Threads[DebugEvent.dwThreadId];
            pThreadInfo->hThread = DebugEvent.u.CreateThread.hThread;
            break;

        case CREATE_PROCESS_DEBUG_EVENT: {
            if (pOptions->debug_flag) {
                lprintf("CREATE_PROCESS PID=%lu TID=%lu\r\n",
                        DebugEvent.dwProcessId,
                        DebugEvent.dwThreadId
                );
            }

            HANDLE hProcess = DebugEvent.u.CreateProcessInfo.hProcess;

            pProcessInfo = &g_Processes[DebugEvent.dwProcessId];
            pProcessInfo->hProcess = hProcess;

            pThreadInfo = &pProcessInfo->Threads[DebugEvent.dwThreadId];
            pThreadInfo->hThread = DebugEvent.u.CreateProcessInfo.hThread;

            DWORD dwSymOptions = SymGetOptions();
            dwSymOptions |=
                SYMOPT_LOAD_LINES |
                SYMOPT_DEFERRED_LOADS;
            if (pOptions->debug_flag) {
                //dwSymOptions |= SYMOPT_DEBUG;
            }

#ifdef _WIN64
            BOOL bWow64 = FALSE;
            IsWow64Process(hProcess, &bWow64);
            if (bWow64) {
                dwSymOptions |= SYMOPT_INCLUDE_32BIT_MODULES;
            }
#endif

            SymSetOptions(dwSymOptions);
            if (!InitializeSym(hProcess, FALSE)) {
                OutputDebug("error: SymInitialize failed: 0x%08lx\n", GetLastError());
                exit(EXIT_FAILURE);
            }

            SymRegisterCallback64(hProcess, &symCallback, 0);

            break;
        }

        case EXIT_THREAD_DEBUG_EVENT:
            if (pOptions->debug_flag) {
                lprintf("EXIT_THREAD PID=%lu TID=%lu dwExitCode=0x%lx\r\n",
                        DebugEvent.dwProcessId,
                        DebugEvent.dwThreadId,
                        DebugEvent.u.ExitThread.dwExitCode
                );
            }

            // Remove the thread from the thread list
            pProcessInfo = &g_Processes[DebugEvent.dwProcessId];
            pProcessInfo->Threads.erase(DebugEvent.dwThreadId);
            break;

        case EXIT_PROCESS_DEBUG_EVENT: {
            if (pOptions->debug_flag) {
                lprintf("EXIT_PROCESS PID=%lu TID=%lu dwExitCode=0x%lx\r\n",
                        DebugEvent.dwProcessId,
                        DebugEvent.dwThreadId,
                        DebugEvent.u.ExitProcess.dwExitCode
                );
            }

            // Remove the process from the process list
            HANDLE hProcess = g_Processes[DebugEvent.dwProcessId].hProcess;
            g_Processes.erase(DebugEvent.dwProcessId);

            if (!SymCleanup(hProcess)) {
                OutputDebug("SymCleanup failed with 0x%08lx\n", GetLastError());
            }

            if (g_Processes.empty()) {
                fFinished = TRUE;
            }

            break;
        }

        case LOAD_DLL_DEBUG_EVENT:
            if (pOptions->debug_flag) {
                lprintf("LOAD_DLL PID=%lu TID=%lu lpBaseOfDll=%p\r\n",
                        DebugEvent.dwProcessId,
                        DebugEvent.dwThreadId,
                        DebugEvent.u.LoadDll.lpBaseOfDll
                );
            }

            break;

        case UNLOAD_DLL_DEBUG_EVENT:
            if (pOptions->debug_flag) {
                lprintf("UNLOAD_DLL PID=%lu TID=%lu lpBaseOfDll=%p\r\n",
                        DebugEvent.dwProcessId,
                        DebugEvent.dwThreadId,
                        DebugEvent.u.UnloadDll.lpBaseOfDll
                );
            }
            break;

        case OUTPUT_DEBUG_STRING_EVENT: {
            if (pOptions->debug_flag) {
                lprintf("OUTPUT_DEBUG_STRING PID=%lu TID=%lu\r\n",
                        DebugEvent.dwProcessId,
                        DebugEvent.dwThreadId
                );
            }

            pProcessInfo = &g_Processes[DebugEvent.dwProcessId];

            assert(!DebugEvent.u.DebugString.fUnicode);

            LPSTR lpDebugStringData = readProcessString(pProcessInfo->hProcess,
                                                        DebugEvent.u.DebugString.lpDebugStringData,
                                                        DebugEvent.u.DebugString.nDebugStringLength);

            fputs(lpDebugStringData, stderr);

            free(lpDebugStringData);
            break;
        }

        case RIP_EVENT:
            if (pOptions->debug_flag) {
                lprintf("RIP PID=%lu TID=%lu\r\n",
                        DebugEvent.dwProcessId,
                        DebugEvent.dwThreadId
                );
            }
            break;

        default:
            if (pOptions->debug_flag) {
                lprintf("EVENT%lu PID=%lu TID=%lu\r\n",
                    DebugEvent.dwDebugEventCode,
                    DebugEvent.dwProcessId,
                    DebugEvent.dwThreadId
                );
            }
            break;
        }

        // Resume executing the thread that reported the debugging event.
        ContinueDebugEvent(
            DebugEvent.dwProcessId,
            DebugEvent.dwThreadId,
            dwContinueStatus
        );
    }

    return TRUE;
}
<commit_msg>debugger: Dump stack on abort.<commit_after>/*
 * Copyright 2002-2013 Jose Fonseca
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <map>

#include <assert.h>
#include <stdlib.h>

#include <windows.h>
#include <tchar.h>
#include <ntstatus.h>

#include "debugger.h"
#include "log.h"
#include "outdbg.h"
#include "symbols.h"

typedef struct {
    HANDLE hThread;
}
THREAD_INFO, * PTHREAD_INFO;

typedef std::map< DWORD, THREAD_INFO > THREAD_INFO_LIST;

typedef struct {
    HANDLE hProcess;
    THREAD_INFO_LIST Threads;
}
PROCESS_INFO, * PPROCESS_INFO;

typedef std::map< DWORD, PROCESS_INFO> PROCESS_INFO_LIST;
static PROCESS_INFO_LIST g_Processes;

BOOL ObtainSeDebugPrivilege(void)
{
    HANDLE hToken;
    PTOKEN_PRIVILEGES NewPrivileges;
    BYTE OldPriv[1024];
    PBYTE pbOldPriv;
    ULONG cbNeeded;
    BOOLEAN fRc;
    LUID LuidPrivilege;

    // Make sure we have access to adjust and to get the old token privileges
    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
    {
        OutputDebug("OpenProcessToken failed with 0x%08lx\n", GetLastError());

        return FALSE;
    }

    cbNeeded = 0;

    // Initialize the privilege adjustment structure
    LookupPrivilegeValue( NULL, SE_DEBUG_NAME, &LuidPrivilege );

    NewPrivileges = (PTOKEN_PRIVILEGES) LocalAlloc(
        LMEM_ZEROINIT,
        sizeof(TOKEN_PRIVILEGES) + (1 - ANYSIZE_ARRAY)*sizeof(LUID_AND_ATTRIBUTES)
    );
    if(NewPrivileges == NULL) {
        return FALSE;
    }

    NewPrivileges->PrivilegeCount = 1;
    NewPrivileges->Privileges[0].Luid = LuidPrivilege;
    NewPrivileges->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

    // Enable the privilege
    pbOldPriv = OldPriv;
    fRc = AdjustTokenPrivileges(
        hToken,
        FALSE,
        NewPrivileges,
        1024,
        (PTOKEN_PRIVILEGES)pbOldPriv,
        &cbNeeded
    );

    if (!fRc) {

        // If the stack was too small to hold the privileges
        // then allocate off the heap
        if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {

            pbOldPriv = (PBYTE)LocalAlloc(LMEM_FIXED, cbNeeded);
            if (pbOldPriv == NULL) {
                return FALSE;
            }

            fRc = AdjustTokenPrivileges(
                hToken,
                FALSE,
                NewPrivileges,
                cbNeeded,
                (PTOKEN_PRIVILEGES)pbOldPriv,
                &cbNeeded
            );
        }
    }
    return fRc;
}

static BOOL CALLBACK
symCallback(HANDLE hProcess,
            ULONG ActionCode,
            ULONG64 CallbackData,
            ULONG64 UserContext)
{
    if (ActionCode == CBA_DEBUG_INFO) {
        lprintf("%s", (LPCSTR)(UINT_PTR)CallbackData);
        return TRUE;
    }

    return FALSE;
}

static char *
readProcessString(HANDLE hProcess, LPCVOID lpBaseAddress, SIZE_T nSize)
{
    LPSTR lpszBuffer = (LPSTR)malloc(nSize + 1);
    SIZE_T NumberOfBytesRead = 0;

    if (!ReadProcessMemory(hProcess, lpBaseAddress, lpszBuffer, nSize, &NumberOfBytesRead)) {
        lpszBuffer[0] = '\0';
    }

    assert(NumberOfBytesRead <= nSize);
    lpszBuffer[NumberOfBytesRead] = '\0';
    return lpszBuffer;
}

BOOL DebugMainLoop(const DebugOptions *pOptions)
{
    BOOL fFinished = FALSE;
    BOOL fBreakpointSignalled = FALSE;
    BOOL fWowBreakpointSignalled = FALSE;

    while(!fFinished)
    {
        DEBUG_EVENT DebugEvent;            // debugging event information
        DWORD dwContinueStatus = DBG_CONTINUE;    // exception continuation
        PPROCESS_INFO pProcessInfo;
        PTHREAD_INFO pThreadInfo;
        HANDLE hProcess;

        // Wait for a debugging event to occur. The second parameter indicates
        // that the function does not return until a debugging event occurs.
        if(!WaitForDebugEvent(&DebugEvent, INFINITE))
        {
            OutputDebug("WaitForDebugEvent: 0x%08lx", GetLastError());

            return FALSE;
        }

        // Process the debugging event code.
        switch (DebugEvent.dwDebugEventCode) {
        case EXCEPTION_DEBUG_EVENT: {
            PEXCEPTION_RECORD pExceptionRecord = &DebugEvent.u.Exception.ExceptionRecord;
            NTSTATUS ExceptionCode = pExceptionRecord->ExceptionCode;

            // Process the exception code. When handling
            // exceptions, remember to set the continuation
            // status parameter (dwContinueStatus). This value
            // is used by the ContinueDebugEvent function.
            if (pOptions->debug_flag) {
                lprintf("EXCEPTION PID=%lu TID=%lu ExceptionCode=0x%lx dwFirstChance=%lu\r\n",
                        DebugEvent.dwProcessId,
                        DebugEvent.dwThreadId,
                        pExceptionRecord->ExceptionCode,
                        DebugEvent.u.Exception.dwFirstChance
                );
            }

            dwContinueStatus = DBG_EXCEPTION_NOT_HANDLED;

            if (DebugEvent.u.Exception.dwFirstChance) {
                if (pExceptionRecord->ExceptionCode == (DWORD)STATUS_BREAKPOINT) {
                    // Signal the aedebug event
                    if (!fBreakpointSignalled) {
                        fBreakpointSignalled = TRUE;

                        if (pOptions->hEvent) {
                            SetEvent(pOptions->hEvent);
                            CloseHandle(pOptions->hEvent);
                        }

                        /*
                         * We ignore first-chance breakpoints by default.
                         *
                         * We get one of these whenever we attach to a process.
                         * But in some cases, we never get a second-chance, e.g.,
                         * when we're attached through MSVCRT's abort().
                         */
                        if (!pOptions->breakpoint_flag) {
                            dwContinueStatus = DBG_CONTINUE;
                            break;
                        }
                    }
                }

                if (ExceptionCode == STATUS_WX86_BREAKPOINT) {
                    if (!fWowBreakpointSignalled) {
                        fWowBreakpointSignalled = TRUE;
                        dwContinueStatus = DBG_CONTINUE;
                    }
                }

                if (!pOptions->first_chance) {
                    break;
                }
            }

            // Find the process in the process list
            pProcessInfo = &g_Processes[DebugEvent.dwProcessId];

            SymRefreshModuleList(pProcessInfo->hProcess);

            dumpException(pProcessInfo->hProcess,
                          &DebugEvent.u.Exception.ExceptionRecord);

            // Find the thread in the thread list
            THREAD_INFO_LIST::const_iterator it;
            for (it = pProcessInfo->Threads.begin(); it != pProcessInfo->Threads.end(); ++it) {
                DWORD dwThreadId = it->first;
                if (dwThreadId != DebugEvent.dwThreadId &&
                    ExceptionCode != STATUS_BREAKPOINT &&
                    !pOptions->verbose_flag) {
                        continue;
                }

                dumpStack(pProcessInfo->hProcess,
                          pThreadInfo->hThread, NULL);
            }

            if (!DebugEvent.u.Exception.dwFirstChance) {
                /*
                 * Terminate the process. As continuing would cause the JIT debugger
                 * to be invoked again.
                 */
                TerminateProcess(pProcessInfo->hProcess, (UINT)ExceptionCode);
            }

            break;
        }

        case CREATE_THREAD_DEBUG_EVENT:
            if (pOptions->debug_flag) {
                lprintf("CREATE_THREAD PID=%lu TID=%lu\r\n",
                        DebugEvent.dwProcessId,
                        DebugEvent.dwThreadId
                );
            }

            // Add the thread to the thread list
            pProcessInfo = &g_Processes[DebugEvent.dwProcessId];
            pThreadInfo = &pProcessInfo->Threads[DebugEvent.dwThreadId];
            pThreadInfo->hThread = DebugEvent.u.CreateThread.hThread;
            break;

        case CREATE_PROCESS_DEBUG_EVENT: {
            if (pOptions->debug_flag) {
                lprintf("CREATE_PROCESS PID=%lu TID=%lu\r\n",
                        DebugEvent.dwProcessId,
                        DebugEvent.dwThreadId
                );
            }

            hProcess = DebugEvent.u.CreateProcessInfo.hProcess;

            pProcessInfo = &g_Processes[DebugEvent.dwProcessId];
            pProcessInfo->hProcess = hProcess;

            pThreadInfo = &pProcessInfo->Threads[DebugEvent.dwThreadId];
            pThreadInfo->hThread = DebugEvent.u.CreateProcessInfo.hThread;

            DWORD dwSymOptions = SymGetOptions();
            dwSymOptions |=
                SYMOPT_LOAD_LINES |
                SYMOPT_DEFERRED_LOADS;
            if (pOptions->debug_flag) {
                //dwSymOptions |= SYMOPT_DEBUG;
            }

#ifdef _WIN64
            BOOL bWow64 = FALSE;
            IsWow64Process(hProcess, &bWow64);
            if (bWow64) {
                dwSymOptions |= SYMOPT_INCLUDE_32BIT_MODULES;
            }
#endif

            SymSetOptions(dwSymOptions);
            if (!InitializeSym(hProcess, FALSE)) {
                OutputDebug("error: SymInitialize failed: 0x%08lx\n", GetLastError());
                exit(EXIT_FAILURE);
            }

            SymRegisterCallback64(hProcess, &symCallback, 0);

            break;
        }

        case EXIT_THREAD_DEBUG_EVENT:
            if (pOptions->debug_flag) {
                lprintf("EXIT_THREAD PID=%lu TID=%lu dwExitCode=0x%lx\r\n",
                        DebugEvent.dwProcessId,
                        DebugEvent.dwThreadId,
                        DebugEvent.u.ExitThread.dwExitCode
                );
            }

            // Remove the thread from the thread list
            pProcessInfo = &g_Processes[DebugEvent.dwProcessId];
            pProcessInfo->Threads.erase(DebugEvent.dwThreadId);
            break;

        case EXIT_PROCESS_DEBUG_EVENT: {
            if (pOptions->debug_flag) {
                lprintf("EXIT_PROCESS PID=%lu TID=%lu dwExitCode=0x%lx\r\n",
                        DebugEvent.dwProcessId,
                        DebugEvent.dwThreadId,
                        DebugEvent.u.ExitProcess.dwExitCode
                );
            }

            pProcessInfo = &g_Processes[DebugEvent.dwProcessId];
            hProcess = pProcessInfo->hProcess;

            // Dump the stack on abort()
            if (DebugEvent.u.ExitProcess.dwExitCode == 3) {
                pThreadInfo = &pProcessInfo->Threads[DebugEvent.dwThreadId];
                dumpStack(hProcess, pThreadInfo->hThread, NULL);
            }

            // Remove the process from the process list
            g_Processes.erase(DebugEvent.dwProcessId);

            if (!SymCleanup(hProcess)) {
                OutputDebug("SymCleanup failed with 0x%08lx\n", GetLastError());
            }

            if (g_Processes.empty()) {
                fFinished = TRUE;
            }

            break;
        }

        case LOAD_DLL_DEBUG_EVENT:
            if (pOptions->debug_flag) {
                lprintf("LOAD_DLL PID=%lu TID=%lu lpBaseOfDll=%p\r\n",
                        DebugEvent.dwProcessId,
                        DebugEvent.dwThreadId,
                        DebugEvent.u.LoadDll.lpBaseOfDll
                );
            }

            break;

        case UNLOAD_DLL_DEBUG_EVENT:
            if (pOptions->debug_flag) {
                lprintf("UNLOAD_DLL PID=%lu TID=%lu lpBaseOfDll=%p\r\n",
                        DebugEvent.dwProcessId,
                        DebugEvent.dwThreadId,
                        DebugEvent.u.UnloadDll.lpBaseOfDll
                );
            }
            break;

        case OUTPUT_DEBUG_STRING_EVENT: {
            if (pOptions->debug_flag) {
                lprintf("OUTPUT_DEBUG_STRING PID=%lu TID=%lu\r\n",
                        DebugEvent.dwProcessId,
                        DebugEvent.dwThreadId
                );
            }

            pProcessInfo = &g_Processes[DebugEvent.dwProcessId];

            assert(!DebugEvent.u.DebugString.fUnicode);

            LPSTR lpDebugStringData = readProcessString(pProcessInfo->hProcess,
                                                        DebugEvent.u.DebugString.lpDebugStringData,
                                                        DebugEvent.u.DebugString.nDebugStringLength);

            fputs(lpDebugStringData, stderr);

            free(lpDebugStringData);
            break;
        }

        case RIP_EVENT:
            if (pOptions->debug_flag) {
                lprintf("RIP PID=%lu TID=%lu\r\n",
                        DebugEvent.dwProcessId,
                        DebugEvent.dwThreadId
                );
            }
            break;

        default:
            if (pOptions->debug_flag) {
                lprintf("EVENT%lu PID=%lu TID=%lu\r\n",
                    DebugEvent.dwDebugEventCode,
                    DebugEvent.dwProcessId,
                    DebugEvent.dwThreadId
                );
            }
            break;
        }

        // Resume executing the thread that reported the debugging event.
        ContinueDebugEvent(
            DebugEvent.dwProcessId,
            DebugEvent.dwThreadId,
            dwContinueStatus
        );
    }

    return TRUE;
}
<|endoftext|>


<commit_before>// (C) 2014 Arek Olek

#pragma once

#include <random>
#include <vector>

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/random_spanning_tree.hpp>
#include <boost/graph/named_function_params.hpp>

#include "range.hpp"

template <class Vertex, class Graph>
Vertex random_neighbor(Vertex const & v, Graph const & G) {
  unsigned target = random<unsigned>(0, out_degree(v, G)-1);
  for(auto w : range(adjacent_vertices(v, G)))
    if(target-- == 0) return w;
  throw "Ouch.";
}

template <class Graph, class Tree>
Tree random_tree(Graph& G) {
  unsigned n = num_vertices(G);
  std::vector<bool> visited(n, false);
  Tree T(n);
  unsigned v = random<unsigned>(0, n-1);
  while(num_edges(T) < n-1) {
    visited[v] = true;
    auto w = random_neighbor(v, G);
    if(!visited[w]) add_edge(v, w, T);
    v = w;
  }
  return T;
}

template <class Graph, class Tree>
Tree wilson_tree(Graph& G) {
  unsigned n = num_vertices(G);
  std::vector<int> pred(n);
  std::default_random_engine gen;
  random_spanning_tree(G, gen, boost::predecessor_map(&pred[0]));
  Tree T(n);
  for(int i = 0; i < n; ++i)
    if(pred[i] != boost::graph_traits<Graph>::null_vertex())
      add_edge(i, pred[i], T);
  return T;
}
<commit_msg>don't reset random engine in subsequent wilson invocations<commit_after>// (C) 2014 Arek Olek

#pragma once

#include <random>
#include <vector>

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/random_spanning_tree.hpp>
#include <boost/graph/named_function_params.hpp>

#include "range.hpp"

template <class Vertex, class Graph>
Vertex random_neighbor(Vertex const & v, Graph const & G) {
  unsigned target = random<unsigned>(0, out_degree(v, G)-1);
  for(auto w : range(adjacent_vertices(v, G)))
    if(target-- == 0) return w;
  throw "Ouch.";
}

template <class Graph, class Tree>
Tree random_tree(Graph& G) {
  unsigned n = num_vertices(G);
  std::vector<bool> visited(n, false);
  Tree T(n);
  unsigned v = random<unsigned>(0, n-1);
  while(num_edges(T) < n-1) {
    visited[v] = true;
    auto w = random_neighbor(v, G);
    if(!visited[w]) add_edge(v, w, T);
    v = w;
  }
  return T;
}

template <class Graph, class Tree>
Tree wilson_tree(Graph& G) {
  unsigned n = num_vertices(G);
  std::vector<int> pred(n);
  static std::default_random_engine gen;
  random_spanning_tree(G, gen, boost::predecessor_map(&pred[0]));
  Tree T(n);
  for(int i = 0; i < n; ++i)
    if(pred[i] != boost::graph_traits<Graph>::null_vertex())
      add_edge(i, pred[i], T);
  return T;
}
<|endoftext|>