diff --git a/hphp/util/stacktrace_profiler.cpp b/hphp/util/stacktrace_profiler.cpp index 35e25bb50..7513f3228 100644 --- a/hphp/util/stacktrace_profiler.cpp +++ b/hphp/util/stacktrace_profiler.cpp @@ -21,9 +21,13 @@ namespace HPHP { -const bool enable = getenv("STACKTRACE_PROFILER") != nullptr; +const bool enabled = getenv("STACKTRACE_PROFILER") != nullptr; const int kMaxDepth = 10; +StackTraceProfiler::StackTraceProfiler(std::string name, int skip) : + m_name(name), finishing(false), m_root(nullptr), m_skip(skip) { +} + // Make a new caller node and link it into n's caller list. StackTraceProfiler::Node* StackTraceProfiler::makeCaller(Node* n, void* addr) { Node* caller = new (m_arena) Node(addr); @@ -54,7 +58,7 @@ StackTraceProfiler::Node* StackTraceProfiler::findCaller(Node* n, void* addr) { } void StackTraceProfiler::count() { - if (!enable) return; + if (!enabled || finishing) return; void* addrs[kMaxDepth]; auto count = backtrace(addrs, kMaxDepth); if (count <= 0) return; @@ -80,12 +84,12 @@ int StackTraceProfiler::numLeaves(Node* n) { } void StackTraceProfiler::print(Node* n, std::string indent) { - fprintf(stderr, "%s%d ", indent.c_str(), n->hits); + fprintf(stderr, "%s%lu ", indent.c_str(), n->hits); if (n->addr) { std::string s = StackTrace::Translate(n->addr)->toString(); fprintf(stderr, "%s\n", s.c_str()); } else { - fprintf(stderr, "%s\n", m_name); + fprintf(stderr, "%s\n", m_name.c_str()); } if (numLeaves(n) <= 1) return; indent += " "; @@ -100,8 +104,76 @@ void StackTraceProfiler::print(Node* n, std::string indent) { } StackTraceProfiler::~StackTraceProfiler() { - if (!enable) return; + if (!enabled) return; + finishing = true; print(&m_root, ""); } +BoolProfiler::BoolProfiler(std::string name) + : name(name) + , p1(name + "=true", 2) + , p0(name + "=false", 2) +{} + +BoolProfiler::~BoolProfiler() { + if (!enabled) return; + auto total = p0.hits() + p1.hits(); + if (total) { + fprintf(stderr, "%s: total=%lu false=%.1f%% true=%.1f%%\n", name.c_str(), + total, + 100.0 * p0.hits() / total, + 100.0 * p1.hits() / total); + } +} + +bool BoolProfiler::operator()(bool b) { + (b ? &p1 : &p0)->count(); + return b; +} + +IntProfiler::IntProfiler(std::string name) + : name(name) + , pN(name + ">=65", 2) + , p64(name + "=33-64", 2) + , p32(name + "=17-32", 2) + , p16(name + "=9-16", 2) + , p8(name + "=5-8", 2) + , p4(name + "=3-4", 2) + , p2(name + "=2", 2) + , p1(name + "=1", 2) + , p0(name + "=0", 2) +{} + +IntProfiler::~IntProfiler() { + if (!enabled) return; + auto total = p0.hits() + p1.hits() + p2.hits() + p4.hits() + p8.hits() + + p16.hits() + p32.hits() + p64.hits() + pN.hits(); + if (total) { + fprintf(stderr, "%s: total=%lu 0=%.1f%% 1=%.1f%% 2=%.1f%% " + "3-4=%.1f%% 5-8=%.1f%% 9-16=%.1f%% 17-32=%.1f%% 33-64=%.1f%% " + "65+=%.1f%%\n", name.c_str(), total, + 100.0 * p0.hits() / total, + 100.0 * p1.hits() / total, + 100.0 * p2.hits() / total, + 100.0 * p4.hits() / total, + 100.0 * p8.hits() / total, + 100.0 * p16.hits() / total, + 100.0 * p32.hits() / total, + 100.0 * p64.hits() / total, + 100.0 * pN.hits() / total); + } +} + +void IntProfiler::operator()(unsigned i) { + (i == 0 ? &p0 : + i == 1 ? &p1 : + i == 2 ? &p2 : + i <= 4 ? &p4 : + i <= 8 ? &p8 : + i <= 16 ? &p16 : + i <= 32 ? &p32 : + i <= 64 ? &p64 : + &pN)->count(); +} + } diff --git a/hphp/util/stacktrace_profiler.h b/hphp/util/stacktrace_profiler.h index 62b8d8773..e91cdb223 100644 --- a/hphp/util/stacktrace_profiler.h +++ b/hphp/util/stacktrace_profiler.h @@ -47,19 +47,18 @@ class StackTraceProfiler { void* const addr; Node* callers; Node* next; - int hits; + size_t hits; }; public: - explicit StackTraceProfiler(const char* name, int skip = 1) : - m_name(name), m_root(nullptr), m_skip(skip) { - } + explicit StackTraceProfiler(std::string name, int skip = 1); ~StackTraceProfiler(); StackTraceProfiler(const StackTraceProfiler& other) = delete; StackTraceProfiler& operator=(const StackTraceProfiler& other) = delete; // count one call in this probe. void count(); + size_t hits() const { return m_root.hits; } private: static bool compareNodes(Node* a, Node* b); @@ -70,12 +69,38 @@ private: private: Arena m_arena; - const char* m_name; + const std::string m_name; std::mutex m_mutex; + std::atomic finishing; Node m_root; int m_skip; }; +/* + * BoolProfiler is used to collect profiled samples of a bool variable. + */ +struct BoolProfiler { + explicit BoolProfiler(std::string name); + ~BoolProfiler(); + bool operator()(bool b); +private: + const std::string name; + StackTraceProfiler p1, p0; +}; + +/* + * IntProfiler is used to collect profiled samples of an unsiged variable. + * A histogram of samples are collected in power-of-2 buckets up to 64. + */ +struct IntProfiler { + explicit IntProfiler(std::string name); + ~IntProfiler(); + void operator()(unsigned i); +private: + const std::string name; + StackTraceProfiler pN, p64, p32, p16, p8, p4, p2, p1, p0; +}; + } #endif