#pragma once #include "V8Tree/Instance.h" #include "V8Tree/Service.h" #include "rbx/RunningAverage.h" #include "rbx/TaskScheduler.h" #include "util/Analytics.h" #include namespace RBX { class DataModel; void registerStatsClasses(); namespace Profiling { class Profiler; } namespace Stats { extern const char* const sStats; extern const char* const sStatsItem; static std::string countersApiKey = "76E5A40C-3AE1-4028-9F10-7C62520BD94F"; class Item : public DescribedNonCreatable { protected: double val; std::string sValue; virtual void update(); public: Item():val(0) {} Item(const char* name):DescribedNonCreatable(name),val(0) {} const std::string& getStringValue() { update(); return sValue; } double getValue() { update(); return val; } // Slower version of getStringValue() for Reflection std::string getStringValue2() { return getStringValue(); } void setValue(double val, const std::string& sValue) { this->val = val; this->sValue = sValue; } void formatMem(size_t bytes); void formatValue(double val, const char* fmt, ...); void formatRate(const RunningAverageTimeInterval& interval); void formatPercent(double val) { formatValue(val, "%.1f%%", 100.0f * val); } // TODO: Merge with StringConverter classes? Reflection code? template void formatValue(const V& value); // Helper functions: Item* createChildItem(const char* name); template Item* createChildItem(const char* name, boost::function0 func); template Item* createBoundChildItem(const char* name, const V& value); Item* createBoundChildItem(const Profiling::Profiler& p); template Item* createBoundChildItem(const char* name, const RunningAverage& ra); template Item* createBoundChildItem(const char* name, const TotalCountTimeInterval& ra); template Item* createBoundChildItem(const char* name, const RunningAverageTimeInterval& ra); Item* createBoundMemChildItem(const char* name, const size_t& v); Item* createBoundPercentChildItem(const char* name, const float& v); protected: bool askAddChild(const Instance* instance) const { return Instance::fastDynamicCast(instance)!=NULL; } }; template class TypedStatsItem : public Item { static const T& deref(const T* value) { return *value; } protected: const boost::function0 func; public: TypedStatsItem(boost::function0 func) :func(func) {} TypedStatsItem(const T* value) // TODO: Does boost have a way of turning a value ref to a function0? :func(boost::bind(&TypedStatsItem::deref, value)) {} void update() { formatValue(func()); } }; class TypedMemItem : public TypedStatsItem { public: TypedMemItem(const size_t* value) :TypedStatsItem(value) { } virtual void update() { formatMem(func()); } }; class TypedPercentItem : public TypedStatsItem { public: TypedPercentItem(const float* value) :TypedStatsItem(value) { } virtual void update() { formatPercent(func()); } }; class JsonWriter { std::stringstream& s; bool& needComma; bool hasNonJsonType; public: JsonWriter(std::stringstream& s, bool& needComma) : s(s), needComma(needComma), hasNonJsonType(false) {}; void writeTableEntries(const Reflection::ValueTable& data); void writeArrayEntries(const Reflection::ValueArray& data); void writeTableEntry(const std::pair& value); void writeKeyValue(const std::pair& value); void writeArrayEntry(const Reflection::Variant& value); void writeValue(const Reflection::Variant& value); bool seenNonJsonType() { return hasNonJsonType; } }; // A generic mechanism for displaying stats (like 3D FPS, network traffic, etc.) class StatsService : public DescribedNonCreatable , public Service { std::string customReportUrl; typedef boost::unordered_map LastReportTimes; LastReportTimes lastReportTimes; rbx::signals::scoped_connection baseUrlConnection; public: StatsService():DescribedNonCreatable("Stats") ,minReportInterval(10) {} std::string reporterType; double minReportInterval; std::string getReportUrl() const; void setReportUrl(std::string url); void report(std::string category, shared_ptr data); void report(std::string category, shared_ptr data, int percentage); // This message reports without doing any local throttling. Do not // use this method if the messages are spammy/frequent. If you are // unsure, start by using the throttled report() method to get a // sense for the frequency of these messages. static void report_BypassThrottlingAndCustomUrl(std::string category, const Reflection::ValueTable& data, const char* optional_Shard = "Client"); void reportTaskScheduler(bool includeJobs); void reportJobsStepWindow(); protected: bool askAddChild(const Instance* instance) const { return Instance::fastDynamicCast(instance)!=NULL; } /*override*/ void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider); private: void reportJob(boost::shared_ptr job, shared_ptr stream, bool& isFirst); bool addHeader(shared_ptr stream); static void addCategoryAndTable(const std::string& category, const Reflection::ValueTable& data, shared_ptr stream); bool checkLastReport(const std::string& category); bool tryToStartScript(); static std::string getDefaultReportUrl(const std::string& baseUrl, const std::string& shard); static void postReportWithUrl(const std::string& url, shared_ptr stream); void postReport(shared_ptr stream); }; template Item* Item::createChildItem(const char* name, boost::function0 func) { shared_ptr item = Creatable::create< TypedStatsItem >(func); item->setName(name); item->setParent(this); return item.get(); } template Item* Item::createBoundChildItem(const char* name, const V& v) { shared_ptr item = Creatable::create< TypedStatsItem >(&v); item->setName(name); item->setParent(this); return item.get(); } void setBrowserTrackerId(const std::string& trackerId); void reportGameStatus(const std::string& status, bool blocking = false); } } // namespace