コード例 #1
0
// Corkscrew and libunwind don't compile on the mac, so create a generic
// map object.
BacktraceMap* BacktraceMap::Create(pid_t pid, bool uncached) {
  BacktraceMap* map = new BacktraceMap(pid);
  if (!map->Build()) {
    delete map;
    return NULL;
  }
  return map;
}
コード例 #2
0
// Corkscrew and libunwind don't compile on the mac, so create a generic
// map object.
BacktraceMap* BacktraceMap::Create(pid_t pid, bool /*uncached*/) {
  BacktraceMap* map = new BacktraceMap(pid);
  if (!map->Build()) {
    delete map;
    return nullptr;
  }
  return map;
}
コード例 #3
0
ファイル: VirtualProfiling.cpp プロジェクト: 191919/fbthrift
/**
 * Print the recorded profiling information to the specified file.
 */
void profile_print_info(FILE* f) {
  typedef std::vector< std::pair<Key, size_t> > BacktraceVector;

  CountGreater is_greater;

  // Grab both locks for the duration of the print operation,
  // to ensure the output is a consistent snapshot of a single point in time
  Guard generic_calls_guard(generic_calls_mutex);
  Guard virtual_calls_guard(virtual_calls_mutex);

  // print the info from generic_calls, sorted by frequency
  //
  // We print the generic_calls info ahead of virtual_calls, since it is more
  // useful in some cases.  All T_GENERIC_PROTOCOL calls can be eliminated
  // from most programs.  Not all T_VIRTUAL_CALLs will be eliminated by
  // converting to templates.
  BacktraceVector gp_sorted(generic_calls.begin(), generic_calls.end());
  std::sort(gp_sorted.begin(), gp_sorted.end(), is_greater);

  for (BacktraceVector::const_iterator it = gp_sorted.begin();
       it != gp_sorted.end();
       ++it) {
    Key const &key = it->first;
    size_t const count = it->second;
    fprintf(f, "T_GENERIC_PROTOCOL: %zu calls to %s with a %s:\n",
            count, key.getTypeName(), key.getTypeName2());
    key.getBacktrace()->print(f, 2);
    fprintf(f, "\n");
  }

  // print the info from virtual_calls, sorted by frequency
  BacktraceVector vc_sorted(virtual_calls.begin(), virtual_calls.end());
  std::sort(vc_sorted.begin(), vc_sorted.end(), is_greater);

  for (BacktraceVector::const_iterator it = vc_sorted.begin();
       it != vc_sorted.end();
       ++it) {
    Key const &key = it->first;
    size_t const count = it->second;
    fprintf(f, "T_VIRTUAL_CALL: %zu calls on %s:\n", count, key.getTypeName());
    key.getBacktrace()->print(f, 2);
    fprintf(f, "\n");
  }
}
コード例 #4
0
ファイル: VirtualProfiling.cpp プロジェクト: 191919/fbthrift
/**
 * Write a BacktraceMap as Google CPU profiler binary data.
 */
static void profile_write_pprof_file(FILE* f, BacktraceMap const& map) {
  // Write the header
  uintptr_t header[5] = { 0, 3, 0, 0, 0 };
  fwrite(&header, sizeof(header), 1, f);

  // Write the profile records
  for (BacktraceMap::const_iterator it = map.begin(); it != map.end(); ++it) {
    uintptr_t count = it->second;
    fwrite(&count, sizeof(count), 1, f);

    Backtrace const* bt = it->first.getBacktrace();
    uintptr_t num_pcs = bt->getDepth();
    fwrite(&num_pcs, sizeof(num_pcs), 1, f);

    for (uintptr_t n = 0; n < num_pcs; ++n) {
      void* pc = bt->getFrame(n);
      fwrite(&pc, sizeof(pc), 1, f);
    }
  }

  // Write the trailer
  uintptr_t trailer[3] = { 0, 1, 0 };
  fwrite(&trailer, sizeof(trailer), 1, f);

  // Write /proc/self/maps
  // TODO(simpkins): This only works on linux
  FILE *proc_maps = fopen("/proc/self/maps", "r");
  if (proc_maps) {
    uint8_t buf[4096];
    while (true) {
      size_t bytes_read = fread(buf, 1, sizeof(buf), proc_maps);
      if (bytes_read == 0) {
        break;
      }
      fwrite(buf, 1, bytes_read, f);
    }
    fclose(proc_maps);
  }
}
コード例 #5
0
ファイル: VirtualProfiling.cpp プロジェクト: 191919/fbthrift
namespace facebook { namespace thrift {

using ::apache::thrift::concurrency::Mutex;
using ::apache::thrift::concurrency::Guard;

static const unsigned int MAX_STACK_DEPTH = 15;

/**
 * A stack trace
 */
class Backtrace {
 public:
  explicit Backtrace(int skip = 0);
  Backtrace(Backtrace const &bt);

  void operator=(Backtrace const &bt) {
    numCallers_ = bt.numCallers_;
    if (numCallers_ >= 0) {
      memcpy(callers_, bt.callers_, numCallers_ * sizeof(void*));
    }
  }

  bool operator==(Backtrace const &bt) const {
    return (cmp(bt) == 0);
  }

  size_t hash() const {
    intptr_t ret = 0;
    for (int n = 0; n < numCallers_; ++n) {
      ret ^= reinterpret_cast<intptr_t>(callers_[n]);
    }
    return static_cast<size_t>(ret);
  }

  int cmp(Backtrace const& bt) const {
    int depth_diff = (numCallers_ - bt.numCallers_);
    if (depth_diff != 0) {
      return depth_diff;
    }

    for (int n = 0; n < numCallers_; ++n) {
      int diff = reinterpret_cast<intptr_t>(callers_[n]) -
        reinterpret_cast<intptr_t>(bt.callers_[n]);
      if (diff != 0) {
        return diff;
      }
    }

    return 0;
  }

  void print(FILE *f, int indent=0, int start=0) const {
    char **strings = backtrace_symbols(callers_, numCallers_);
    if (strings) {
      start += skip_;
      if (start < 0) {
        start = 0;
      }
      for (int n = start; n < numCallers_; ++n) {
        fprintf(f, "%*s#%-2d %s\n", indent, "", n, strings[n]);
      }
      free(strings);
    } else {
      fprintf(f, "%*s<failed to determine symbols>\n", indent, "");
    }
  }

  int getDepth() const {
    return numCallers_ - skip_;
  }

  void *getFrame(int index) const {
    int adjusted_index = index + skip_;
    if (adjusted_index < 0 || adjusted_index >= numCallers_) {
      return nullptr;
    }
    return callers_[adjusted_index];
  }

 private:
  void *callers_[MAX_STACK_DEPTH];
  int numCallers_;
  int skip_;
};

// Define the constructors non-inline, so they consistently add a single
// frame to the stack trace, regardless of whether optimization is enabled
Backtrace::Backtrace(int skip)
  : skip_(skip + 1) // ignore the constructor itself
{
  numCallers_ = backtrace(callers_, MAX_STACK_DEPTH);
  if (skip_ > numCallers_) {
    skip_ = numCallers_;
  }
}

Backtrace::Backtrace(Backtrace const &bt)
  : numCallers_(bt.numCallers_)
  , skip_(bt.skip_) {
  if (numCallers_ >= 0) {
    memcpy(callers_, bt.callers_, numCallers_ * sizeof(void*));
  }
}

/**
 * A backtrace, plus one or two type names
 */
class Key {
 public:
  class Hash {
   public:
    size_t operator()(Key const& k) const {
      return k.hash();
    }
  };

  Key(const Backtrace* bt, const std::type_info& type_info)
    : backtrace_(bt)
    , typeName1_(type_info.name())
    , typeName2_(nullptr) {
  }

  Key(const Backtrace* bt, const std::type_info& type_info1,
      const std::type_info& type_info2)
    : backtrace_(bt)
    , typeName1_(type_info1.name())
    , typeName2_(type_info2.name()) {
  }

  Key(const Key& k)
    : backtrace_(k.backtrace_)
    , typeName1_(k.typeName1_)
    , typeName2_(k.typeName2_) {
  }

  void operator=(const Key& k) {
    backtrace_ = k.backtrace_;
    typeName1_ = k.typeName1_;
    typeName2_ = k.typeName2_;
  }

  const Backtrace* getBacktrace() const {
    return backtrace_;
  }

  const char* getTypeName() const {
    return typeName1_;
  }

  const char* getTypeName2() const {
    return typeName2_;
  }

  void makePersistent() {
    // Copy the Backtrace object
    backtrace_ = new Backtrace(*backtrace_);

    // NOTE: We don't copy the type name.
    // The GNU libstdc++ implementation of type_info::name() returns a value
    // that will be valid for the lifetime of the program.  (Although the C++
    // standard doesn't guarantee this will be true on all implementations.)
  }

  /**
   * Clean up memory allocated by makePersistent()
   *
   * Should only be invoked if makePersistent() has previously been called.
   * The Key should no longer be used after cleanup() is called.
   */
  void cleanup() {
    delete backtrace_;
    backtrace_ = nullptr;
  }

  int cmp(const Key& k) const {
    int ret = backtrace_->cmp(*k.backtrace_);
    if (ret != 0) {
      return ret;
    }

    // NOTE: We compare just the name pointers.
    // With GNU libstdc++, every type_info object for the same type points to
    // exactly the same name string.  (Although this isn't guaranteed by the
    // C++ standard.)
    ret = k.typeName1_ - typeName1_;
    if (ret != 0) {
      return ret;
    }
    return k.typeName2_ - typeName2_;
  }

  bool operator==(const Key& k) const {
    return cmp(k) == 0;
  }

  size_t hash() const {
    // NOTE: As above, we just use the name pointer value.
    // Works with GNU libstdc++, but not guaranteed to be correct on all
    // implementations.
    return backtrace_->hash() ^
      reinterpret_cast<size_t>(typeName1_) ^
      reinterpret_cast<size_t>(typeName2_);
  }

 private:
  const Backtrace* backtrace_;
  const char* typeName1_;
  const char* typeName2_;
};

/**
 * A functor that determines which of two BacktraceMap entries
 * has a higher count.
 */
class CountGreater {
 public:
  bool operator()(std::pair<Key, size_t> bt1,
                  std::pair<Key, size_t> bt2) const {
    return bt1.second > bt2.second;
  }
};

typedef __gnu_cxx::hash_map<Key, size_t, Key::Hash> BacktraceMap;

/**
 * A map describing how many times T_VIRTUAL_CALL() has been invoked.
 */
BacktraceMap virtual_calls;
Mutex virtual_calls_mutex;

/**
 * A map describing how many times T_GENERIC_PROTOCOL() has been invoked.
 */
BacktraceMap generic_calls;
Mutex generic_calls_mutex;


void _record_backtrace(BacktraceMap* map, const Mutex& mutex, Key *k) {
  Guard guard(mutex);

  BacktraceMap::iterator it = map->find(*k);
  if (it == map->end()) {
    k->makePersistent();
    map->insert(std::make_pair(*k, 1));
  } else {
    // increment the count
    // NOTE: we could assert if it->second is 0 afterwards, since that would
    // mean we've wrapped.
    ++(it->second);
  }
}

/**
 * Record an unnecessary virtual function call.
 *
 * This method is invoked by the T_VIRTUAL_CALL() macro.
 */
void profile_virtual_call(const std::type_info& type) {
  int const skip = 1; // ignore this frame
  Backtrace bt(skip);
  Key k(&bt, type);
  _record_backtrace(&virtual_calls, virtual_calls_mutex, &k);
}

/**
 * Record a call to a template processor with a protocol that is not the one
 * specified in the template parameter.
 *
 * This method is invoked by the T_GENERIC_PROTOCOL() macro.
 */
void profile_generic_protocol(const std::type_info& template_type,
                              const std::type_info& prot_type) {
  int const skip = 1; // ignore this frame
  Backtrace bt(skip);
  Key k(&bt, template_type, prot_type);
  _record_backtrace(&generic_calls, generic_calls_mutex, &k);
}

/**
 * Print the recorded profiling information to the specified file.
 */
void profile_print_info(FILE* f) {
  typedef std::vector< std::pair<Key, size_t> > BacktraceVector;

  CountGreater is_greater;

  // Grab both locks for the duration of the print operation,
  // to ensure the output is a consistent snapshot of a single point in time
  Guard generic_calls_guard(generic_calls_mutex);
  Guard virtual_calls_guard(virtual_calls_mutex);

  // print the info from generic_calls, sorted by frequency
  //
  // We print the generic_calls info ahead of virtual_calls, since it is more
  // useful in some cases.  All T_GENERIC_PROTOCOL calls can be eliminated
  // from most programs.  Not all T_VIRTUAL_CALLs will be eliminated by
  // converting to templates.
  BacktraceVector gp_sorted(generic_calls.begin(), generic_calls.end());
  std::sort(gp_sorted.begin(), gp_sorted.end(), is_greater);

  for (BacktraceVector::const_iterator it = gp_sorted.begin();
       it != gp_sorted.end();
       ++it) {
    Key const &key = it->first;
    size_t const count = it->second;
    fprintf(f, "T_GENERIC_PROTOCOL: %zu calls to %s with a %s:\n",
            count, key.getTypeName(), key.getTypeName2());
    key.getBacktrace()->print(f, 2);
    fprintf(f, "\n");
  }

  // print the info from virtual_calls, sorted by frequency
  BacktraceVector vc_sorted(virtual_calls.begin(), virtual_calls.end());
  std::sort(vc_sorted.begin(), vc_sorted.end(), is_greater);

  for (BacktraceVector::const_iterator it = vc_sorted.begin();
       it != vc_sorted.end();
       ++it) {
    Key const &key = it->first;
    size_t const count = it->second;
    fprintf(f, "T_VIRTUAL_CALL: %zu calls on %s:\n", count, key.getTypeName());
    key.getBacktrace()->print(f, 2);
    fprintf(f, "\n");
  }
}

/**
 * Print the recorded profiling information to stdout.
 */
void profile_print_info() {
  profile_print_info(stdout);
}

/**
 * Write a BacktraceMap as Google CPU profiler binary data.
 */
static void profile_write_pprof_file(FILE* f, BacktraceMap const& map) {
  // Write the header
  uintptr_t header[5] = { 0, 3, 0, 0, 0 };
  fwrite(&header, sizeof(header), 1, f);

  // Write the profile records
  for (BacktraceMap::const_iterator it = map.begin(); it != map.end(); ++it) {
    uintptr_t count = it->second;
    fwrite(&count, sizeof(count), 1, f);

    Backtrace const* bt = it->first.getBacktrace();
    uintptr_t num_pcs = bt->getDepth();
    fwrite(&num_pcs, sizeof(num_pcs), 1, f);

    for (uintptr_t n = 0; n < num_pcs; ++n) {
      void* pc = bt->getFrame(n);
      fwrite(&pc, sizeof(pc), 1, f);
    }
  }

  // Write the trailer
  uintptr_t trailer[3] = { 0, 1, 0 };
  fwrite(&trailer, sizeof(trailer), 1, f);

  // Write /proc/self/maps
  // TODO(simpkins): This only works on linux
  FILE *proc_maps = fopen("/proc/self/maps", "r");
  if (proc_maps) {
    uint8_t buf[4096];
    while (true) {
      size_t bytes_read = fread(buf, 1, sizeof(buf), proc_maps);
      if (bytes_read == 0) {
        break;
      }
      fwrite(buf, 1, bytes_read, f);
    }
    fclose(proc_maps);
  }
}

/**
 * Write the recorded profiling information as pprof files.
 *
 * This writes the information using the Google CPU profiler binary data
 * format, so it can be analyzed with pprof.  Note that information about the
 * protocol/transport data types cannot be stored in this file format.
 *
 * See http://code.google.com/p/google-perftools/ for more details.
 *
 * @param gen_calls_f     The information about calls to
 *                        profile_generic_protocol() will be written to this
 *                        file.
 * @param virtual_calls_f The information about calls to
 *                        profile_virtual_call() will be written to this file.
 */
void profile_write_pprof(FILE* gen_calls_f, FILE* virtual_calls_f) {
  typedef std::vector< std::pair<Key, size_t> > BacktraceVector;

  CountGreater is_greater;

  // Grab both locks for the duration of the print operation,
  // to ensure the output is a consistent snapshot of a single point in time
  Guard generic_calls_guard(generic_calls_mutex);
  Guard virtual_calls_guard(virtual_calls_mutex);

  // write the info from generic_calls
  profile_write_pprof_file(gen_calls_f, generic_calls);

  // write the info from virtual_calls
  profile_write_pprof_file(virtual_calls_f, virtual_calls);
}

}} // apache::thrift