Expand jit documentation a bit
[hiphop-php.git] / hphp / util / service-data.cpp
blobe7f33e8a368a25f7b86bb116ec46ebeccfb68ae5
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2014 Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/util/service-data.h"
19 #include <array>
20 #include <memory>
21 #include <vector>
22 #include <tbb/concurrent_unordered_map.h>
24 #include "folly/Conv.h"
25 #include "folly/MapUtil.h"
26 #include "folly/stats/Histogram-defs.h"
28 #include "hphp/util/portability.h"
30 namespace HPHP {
32 //////////////////////////////////////////////////////////////////////
34 namespace ServiceData {
36 ExportedTimeSeries::ExportedTimeSeries(
37 int numBuckets,
38 const std::vector<std::chrono::seconds>& durations,
39 const std::vector<StatsType>& exportTypes)
40 : m_timeseries(folly::MultiLevelTimeSeries<int64_t>(numBuckets,
41 durations.size(),
42 &durations[0])),
43 m_exportTypes(exportTypes) {
46 void ExportedTimeSeries::exportAll(const std::string& prefix,
47 std::map<std::string, int64_t>& statsMap) {
48 SYNCHRONIZED(m_timeseries) {
49 // must first call update to flush data.
50 m_timeseries.update(detail::nowAsSeconds());
52 for (int i = 0; i < m_timeseries.numLevels(); ++i) {
53 auto& level = m_timeseries.getLevel(i);
54 std::string suffix =
55 level.isAllTime() ? "" :
56 folly::to<std::string>(".", level.duration().count());
58 for (auto type : m_exportTypes) {
59 if (type == ServiceData::StatsType::AVG) {
60 statsMap.insert(
61 std::make_pair(folly::to<std::string>(prefix, ".avg", suffix),
62 level.avg()));
63 } else if (type == ServiceData::StatsType::SUM) {
64 statsMap.insert(
65 std::make_pair(folly::to<std::string>(prefix, ".sum", suffix),
66 level.sum()));
67 } else if (type == ServiceData::StatsType::RATE) {
68 statsMap.insert(
69 std::make_pair(folly::to<std::string>(prefix, ".rate", suffix),
70 level.rate()));
71 } else if (type == ServiceData::StatsType::COUNT) {
72 statsMap.insert(
73 std::make_pair(folly::to<std::string>(prefix, ".count", suffix),
74 level.count()));
75 } else if (type == ServiceData::StatsType::PCT) {
76 statsMap.insert(
77 std::make_pair(folly::to<std::string>(prefix, ".pct", suffix),
78 level.avg() * 100));
85 ExportedHistogram::ExportedHistogram(
86 int64_t bucketSize,
87 int64_t min,
88 int64_t max,
89 const std::vector<double>& exportPercentiles)
90 : m_histogram(folly::Histogram<int64_t>(bucketSize, min, max)),
91 m_exportPercentiles(exportPercentiles) {
94 void ExportedHistogram::exportAll(const std::string& prefix,
95 std::map<std::string, int64_t>& statsMap) {
96 SYNCHRONIZED(m_histogram) {
97 for (double percentile : m_exportPercentiles) {
98 statsMap.insert(
99 std::make_pair(
100 folly::to<std::string>(
101 prefix, ".hist.p", folly::to<int32_t>(percentile * 100)),
102 m_histogram.getPercentileEstimate(percentile)));
107 namespace detail {
108 template <class ClassWithPrivateDestructor>
109 class FriendDeleter {
110 public:
111 template <class... Args>
112 explicit FriendDeleter(Args&&... args)
113 : m_instance(new ClassWithPrivateDestructor(
114 std::forward<Args>(args)...)) {}
115 ~FriendDeleter() { delete m_instance; }
117 ClassWithPrivateDestructor* get() const { return m_instance; }
118 ClassWithPrivateDestructor* release() {
119 auto r = m_instance;
120 m_instance = nullptr;
121 return r;
124 private:
125 ClassWithPrivateDestructor* m_instance;
127 } // namespace detail
129 namespace {
131 // Find 'key' in concurrent_unordered_map 'map'. Return true iff the key is
132 // found.
133 template<class Key, class Value>
134 bool concurrentMapGet(const tbb::concurrent_unordered_map<Key, Value>& map,
135 const Key& key,
136 Value& value) {
137 auto iterator = map.find(key);
138 if (iterator != map.end()) {
139 value = iterator->second;
140 return true;
142 return false;
145 // Find or insert 'key' into concurrent_unordered_map 'map'.
147 // Return the value pointer from 'map' if it exists. Otherwise, insert it into
148 // the map by creating a new object on the heap using the supplied arguments.
150 // Note that this function could be called concurrently. If the insertion to
151 // 'map' is successful, we release the ownership of value object from
152 // valuePtr. If the key is already in the map because someone else beat us to
153 // the insertion, we will return the existing value and delete the object we
154 // created.
156 template <class Key, class Value, class... Args>
157 Value* getOrCreateWithArgs(tbb::concurrent_unordered_map<Key, Value*>& map,
158 const Key& key,
159 Args&&... args) {
160 // Optimistic case: the object might already be created. Do a simple look
161 // up.
162 Value* ret = nullptr;
163 if (concurrentMapGet(map, key, ret)) {
164 return ret;
167 // We didn't find an existing value for the key. Create it. Hold the new
168 // object in a deleter and release it later if the insert is successful.
169 detail::FriendDeleter<Value> deleter(std::forward<Args>(args)...);
171 auto result = map.insert(std::make_pair(key, deleter.get()));
172 if (result.second) {
173 // insert successfully. release the memory.
174 deleter.release();
175 } else {
176 // key is already inserted. This can happen if two threads were racing
177 // to create the counter. In this case, nothing further needs to be done.
178 // valuePtr's object will get destroyed when we go out of scope.
180 return result.first->second;
183 class Impl {
184 public:
185 ExportedCounter* createCounter(const std::string& name) {
186 return getOrCreateWithArgs(m_counterMap, name);
189 ExportedTimeSeries* createTimeseries(
190 const std::string& name,
191 const std::vector<ServiceData::StatsType>& types,
192 const std::vector<std::chrono::seconds>& levels,
193 int numBuckets) {
194 return getOrCreateWithArgs(
195 m_timeseriesMap, name, numBuckets, levels, types);
198 ExportedHistogram* createHistogram(
199 const std::string& name,
200 int64_t bucketSize,
201 int64_t min,
202 int64_t max,
203 const std::vector<double>& exportPercentiles) {
204 return getOrCreateWithArgs(
205 m_histogramMap, name, bucketSize, min, max, exportPercentiles);
208 void exportAll(std::map<std::string, int64_t>& statsMap) {
209 for (auto& counter : m_counterMap) {
210 statsMap.insert(std::make_pair(counter.first,
211 counter.second->getValue()));
214 for (auto& ts : m_timeseriesMap) {
215 ts.second->exportAll(ts.first, statsMap);
218 for (auto& histogram : m_histogramMap) {
219 histogram.second->exportAll(histogram.first, statsMap);
223 private:
224 // This is a singleton class. Once constructed, we never destroy it. See the
225 // implementation note below.
226 ~Impl() = delete;
228 // Delete all the values from a STL style associative container.
229 template <typename Container>
230 static void containerDeleteSeconds(Container* container) {
231 for (auto iter : *container) {
232 delete iter.second;
233 iter.second = 0;
237 typedef tbb::concurrent_unordered_map<std::string, ExportedCounter*>
238 ExportedCounterMap;
239 typedef tbb::concurrent_unordered_map<std::string, ExportedTimeSeries*>
240 ExportedTimeSeriesMap;
241 typedef tbb::concurrent_unordered_map<std::string, ExportedHistogram*>
242 ExportedHistogramMap;
244 ExportedCounterMap m_counterMap;
245 ExportedTimeSeriesMap m_timeseriesMap;
246 ExportedHistogramMap m_histogramMap;
249 // Implementation note:
251 // Impl data structure is a singleton and globally accessible. We need to
252 // initialize it before anyone tries to use it. It is possible and likely that
253 // another statically initialized object will call methods on it to create
254 // counters. Therefore, we need Impl to be initialized statically before main()
255 // starts. Unfortunately, there is no initialization order guarantees for the
256 // statically and globally constructed objects. To get around that, we wrap the
257 // initialization in a function so s_impl will get initialized the first time it
258 // gets called.
260 // For the same reason, we need s_impl to be destructed after all other
261 // statically created objects may reference it in their destructor. We achieve
262 // that by *intentionally* creating the object on heap and never delete it. It's
263 // better to leak memory here than to have random crashes on shutdown.
264 static Impl& getServiceDataInstance() {
265 static Impl *s_impl = new Impl();
266 return *s_impl;
268 // One problem with getServiceDataInstance() is that it's not thread safe. If
269 // two threads are accessing this function for the first time concurrently, we
270 // might end up creating two Impl object. We work around that by making sure we
271 // trigger this function statically before main() starts.
273 // Note that it's still possible for the race condition to happen if we are
274 // creating and starting threads statically before main() starts. If that
275 // happens, we'll have to wrap getServiceDataInstance around a pthread_once and
276 // pay some runtime synchronization cost.
277 UNUSED const Impl& s_dummy = getServiceDataInstance();
279 } // namespace
281 ExportedCounter* createCounter(const std::string& name) {
282 return getServiceDataInstance().createCounter(name);
285 ExportedTimeSeries* createTimeseries(
286 const std::string& name,
287 const std::vector<ServiceData::StatsType>& types,
288 const std::vector<std::chrono::seconds>& levels,
289 int numBuckets) {
290 return getServiceDataInstance().createTimeseries(
291 name, types, levels, numBuckets);
294 ExportedHistogram* createHistogram(
295 const std::string& name,
296 int64_t bucketSize,
297 int64_t min,
298 int64_t max,
299 const std::vector<double>& exportPercentile) {
300 return getServiceDataInstance().createHistogram(
301 name, bucketSize, min, max, exportPercentile);
304 void exportAll(std::map<std::string, int64_t>& statsMap) {
305 return getServiceDataInstance().exportAll(statsMap);
308 } // namespace ServiceData.
310 //////////////////////////////////////////////////////////////////////