David Reiss | c3b3622 | 2010-10-06 17:10:10 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Licensed to the Apache Software Foundation (ASF) under one |
| 3 | * or more contributor license agreements. See the NOTICE file |
| 4 | * distributed with this work for additional information |
| 5 | * regarding copyright ownership. The ASF licenses this file |
| 6 | * to you under the Apache License, Version 2.0 (the |
| 7 | * "License"); you may not use this file except in compliance |
| 8 | * with the License. You may obtain a copy of the License at |
| 9 | * |
| 10 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 11 | * |
| 12 | * Unless required by applicable law or agreed to in writing, |
| 13 | * software distributed under the License is distributed on an |
| 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| 15 | * KIND, either express or implied. See the License for the |
| 16 | * specific language governing permissions and limitations |
| 17 | * under the License. |
| 18 | */ |
| 19 | |
Roger Meier | 49ff8b1 | 2012-04-13 09:12:31 +0000 | [diff] [blame] | 20 | #include <thrift/Thrift.h> |
David Reiss | c3b3622 | 2010-10-06 17:10:10 +0000 | [diff] [blame] | 21 | |
| 22 | // Do nothing if virtual call profiling is not enabled |
| 23 | #if T_GLOBAL_DEBUG_VIRTUAL > 1 |
| 24 | |
| 25 | // TODO: This code only works with g++ (since we rely on the fact |
| 26 | // that all std::type_info instances referring to a particular type |
| 27 | // always return the exact same pointer value from name().) |
| 28 | #ifndef __GNUG__ |
| 29 | #error "Thrift virtual function profiling currently only works with gcc" |
| 30 | #endif // !__GNUG__ |
| 31 | |
| 32 | // TODO: We also require glibc for the backtrace() and backtrace_symbols() |
| 33 | // functions. |
| 34 | #ifndef __GLIBC__ |
| 35 | #error "Thrift virtual function profiling currently requires glibc" |
| 36 | #endif // !__GLIBC__ |
| 37 | |
Roger Meier | 49ff8b1 | 2012-04-13 09:12:31 +0000 | [diff] [blame] | 38 | #include <thrift/concurrency/Mutex.h> |
David Reiss | c3b3622 | 2010-10-06 17:10:10 +0000 | [diff] [blame] | 39 | |
| 40 | #include <ext/hash_map> |
| 41 | #include <execinfo.h> |
| 42 | #include <stdio.h> |
| 43 | |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 44 | namespace apache { |
| 45 | namespace thrift { |
David Reiss | c3b3622 | 2010-10-06 17:10:10 +0000 | [diff] [blame] | 46 | |
| 47 | using ::apache::thrift::concurrency::Mutex; |
| 48 | using ::apache::thrift::concurrency::Guard; |
| 49 | |
| 50 | static const unsigned int MAX_STACK_DEPTH = 15; |
| 51 | |
| 52 | /** |
| 53 | * A stack trace |
| 54 | */ |
| 55 | class Backtrace { |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 56 | public: |
David Reiss | c3b3622 | 2010-10-06 17:10:10 +0000 | [diff] [blame] | 57 | Backtrace(int skip = 0); |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 58 | Backtrace(Backtrace const& bt); |
David Reiss | c3b3622 | 2010-10-06 17:10:10 +0000 | [diff] [blame] | 59 | |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 60 | void operator=(Backtrace const& bt) { |
David Reiss | c3b3622 | 2010-10-06 17:10:10 +0000 | [diff] [blame] | 61 | numCallers_ = bt.numCallers_; |
| 62 | if (numCallers_ >= 0) { |
| 63 | memcpy(callers_, bt.callers_, numCallers_ * sizeof(void*)); |
| 64 | } |
| 65 | } |
| 66 | |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 67 | bool operator==(Backtrace const& bt) const { return (cmp(bt) == 0); } |
David Reiss | c3b3622 | 2010-10-06 17:10:10 +0000 | [diff] [blame] | 68 | |
| 69 | size_t hash() const { |
| 70 | intptr_t ret = 0; |
| 71 | for (int n = 0; n < numCallers_; ++n) { |
| 72 | ret ^= reinterpret_cast<intptr_t>(callers_[n]); |
| 73 | } |
| 74 | return static_cast<size_t>(ret); |
| 75 | } |
| 76 | |
| 77 | int cmp(Backtrace const& bt) const { |
| 78 | int depth_diff = (numCallers_ - bt.numCallers_); |
| 79 | if (depth_diff != 0) { |
| 80 | return depth_diff; |
| 81 | } |
| 82 | |
| 83 | for (int n = 0; n < numCallers_; ++n) { |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 84 | int diff = reinterpret_cast<intptr_t>(callers_[n]) |
| 85 | - reinterpret_cast<intptr_t>(bt.callers_[n]); |
David Reiss | c3b3622 | 2010-10-06 17:10:10 +0000 | [diff] [blame] | 86 | if (diff != 0) { |
| 87 | return diff; |
| 88 | } |
| 89 | } |
| 90 | |
| 91 | return 0; |
| 92 | } |
| 93 | |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 94 | void print(FILE* f, int indent = 0, int start = 0) const { |
| 95 | char** strings = backtrace_symbols(callers_, numCallers_); |
David Reiss | c3b3622 | 2010-10-06 17:10:10 +0000 | [diff] [blame] | 96 | if (strings) { |
| 97 | start += skip_; |
| 98 | if (start < 0) { |
| 99 | start = 0; |
| 100 | } |
| 101 | for (int n = start; n < numCallers_; ++n) { |
| 102 | fprintf(f, "%*s#%-2d %s\n", indent, "", n, strings[n]); |
| 103 | } |
| 104 | free(strings); |
| 105 | } else { |
| 106 | fprintf(f, "%*s<failed to determine symbols>\n", indent, ""); |
| 107 | } |
| 108 | } |
| 109 | |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 110 | int getDepth() const { return numCallers_ - skip_; } |
David Reiss | c3b3622 | 2010-10-06 17:10:10 +0000 | [diff] [blame] | 111 | |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 112 | void* getFrame(int index) const { |
David Reiss | c3b3622 | 2010-10-06 17:10:10 +0000 | [diff] [blame] | 113 | int adjusted_index = index + skip_; |
| 114 | if (adjusted_index < 0 || adjusted_index >= numCallers_) { |
zeshuai007 | 26681fb | 2020-06-03 17:24:38 +0800 | [diff] [blame] | 115 | return nullptr; |
David Reiss | c3b3622 | 2010-10-06 17:10:10 +0000 | [diff] [blame] | 116 | } |
| 117 | return callers_[adjusted_index]; |
| 118 | } |
| 119 | |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 120 | private: |
| 121 | void* callers_[MAX_STACK_DEPTH]; |
David Reiss | c3b3622 | 2010-10-06 17:10:10 +0000 | [diff] [blame] | 122 | int numCallers_; |
| 123 | int skip_; |
| 124 | }; |
| 125 | |
| 126 | // Define the constructors non-inline, so they consistently add a single |
| 127 | // frame to the stack trace, regardless of whether optimization is enabled |
| 128 | Backtrace::Backtrace(int skip) |
| 129 | : skip_(skip + 1) // ignore the constructor itself |
| 130 | { |
| 131 | numCallers_ = backtrace(callers_, MAX_STACK_DEPTH); |
| 132 | if (skip_ > numCallers_) { |
| 133 | skip_ = numCallers_; |
| 134 | } |
| 135 | } |
| 136 | |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 137 | Backtrace::Backtrace(Backtrace const& bt) : numCallers_(bt.numCallers_), skip_(bt.skip_) { |
David Reiss | c3b3622 | 2010-10-06 17:10:10 +0000 | [diff] [blame] | 138 | if (numCallers_ >= 0) { |
| 139 | memcpy(callers_, bt.callers_, numCallers_ * sizeof(void*)); |
| 140 | } |
| 141 | } |
| 142 | |
| 143 | /** |
| 144 | * A backtrace, plus one or two type names |
| 145 | */ |
| 146 | class Key { |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 147 | public: |
David Reiss | c3b3622 | 2010-10-06 17:10:10 +0000 | [diff] [blame] | 148 | class Hash { |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 149 | public: |
| 150 | size_t operator()(Key const& k) const { return k.hash(); } |
David Reiss | c3b3622 | 2010-10-06 17:10:10 +0000 | [diff] [blame] | 151 | }; |
| 152 | |
| 153 | Key(const Backtrace* bt, const std::type_info& type_info) |
zeshuai007 | 26681fb | 2020-06-03 17:24:38 +0800 | [diff] [blame] | 154 | : backtrace_(bt), typeName1_(type_info.name()), typeName2_(nullptr) {} |
David Reiss | c3b3622 | 2010-10-06 17:10:10 +0000 | [diff] [blame] | 155 | |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 156 | Key(const Backtrace* bt, const std::type_info& type_info1, const std::type_info& type_info2) |
| 157 | : backtrace_(bt), typeName1_(type_info1.name()), typeName2_(type_info2.name()) {} |
David Reiss | c3b3622 | 2010-10-06 17:10:10 +0000 | [diff] [blame] | 158 | |
| 159 | Key(const Key& k) |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 160 | : backtrace_(k.backtrace_), typeName1_(k.typeName1_), typeName2_(k.typeName2_) {} |
David Reiss | c3b3622 | 2010-10-06 17:10:10 +0000 | [diff] [blame] | 161 | |
| 162 | void operator=(const Key& k) { |
| 163 | backtrace_ = k.backtrace_; |
| 164 | typeName1_ = k.typeName1_; |
| 165 | typeName2_ = k.typeName2_; |
| 166 | } |
| 167 | |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 168 | const Backtrace* getBacktrace() const { return backtrace_; } |
David Reiss | c3b3622 | 2010-10-06 17:10:10 +0000 | [diff] [blame] | 169 | |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 170 | const char* getTypeName() const { return typeName1_; } |
David Reiss | c3b3622 | 2010-10-06 17:10:10 +0000 | [diff] [blame] | 171 | |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 172 | const char* getTypeName2() const { return typeName2_; } |
David Reiss | c3b3622 | 2010-10-06 17:10:10 +0000 | [diff] [blame] | 173 | |
| 174 | void makePersistent() { |
| 175 | // Copy the Backtrace object |
| 176 | backtrace_ = new Backtrace(*backtrace_); |
| 177 | |
| 178 | // NOTE: We don't copy the type name. |
| 179 | // The GNU libstdc++ implementation of type_info::name() returns a value |
| 180 | // that will be valid for the lifetime of the program. (Although the C++ |
| 181 | // standard doesn't guarantee this will be true on all implementations.) |
| 182 | } |
| 183 | |
| 184 | /** |
| 185 | * Clean up memory allocated by makePersistent() |
| 186 | * |
| 187 | * Should only be invoked if makePersistent() has previously been called. |
| 188 | * The Key should no longer be used after cleanup() is called. |
| 189 | */ |
| 190 | void cleanup() { |
| 191 | delete backtrace_; |
zeshuai007 | 26681fb | 2020-06-03 17:24:38 +0800 | [diff] [blame] | 192 | backtrace_ = nullptr; |
David Reiss | c3b3622 | 2010-10-06 17:10:10 +0000 | [diff] [blame] | 193 | } |
| 194 | |
| 195 | int cmp(const Key& k) const { |
| 196 | int ret = backtrace_->cmp(*k.backtrace_); |
| 197 | if (ret != 0) { |
| 198 | return ret; |
| 199 | } |
| 200 | |
| 201 | // NOTE: We compare just the name pointers. |
| 202 | // With GNU libstdc++, every type_info object for the same type points to |
| 203 | // exactly the same name string. (Although this isn't guaranteed by the |
| 204 | // C++ standard.) |
| 205 | ret = k.typeName1_ - typeName1_; |
| 206 | if (ret != 0) { |
| 207 | return ret; |
| 208 | } |
| 209 | return k.typeName2_ - typeName2_; |
| 210 | } |
| 211 | |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 212 | bool operator==(const Key& k) const { return cmp(k) == 0; } |
David Reiss | c3b3622 | 2010-10-06 17:10:10 +0000 | [diff] [blame] | 213 | |
| 214 | size_t hash() const { |
| 215 | // NOTE: As above, we just use the name pointer value. |
| 216 | // Works with GNU libstdc++, but not guaranteed to be correct on all |
| 217 | // implementations. |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 218 | return backtrace_->hash() ^ reinterpret_cast<size_t>(typeName1_) |
| 219 | ^ reinterpret_cast<size_t>(typeName2_); |
David Reiss | c3b3622 | 2010-10-06 17:10:10 +0000 | [diff] [blame] | 220 | } |
| 221 | |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 222 | private: |
David Reiss | c3b3622 | 2010-10-06 17:10:10 +0000 | [diff] [blame] | 223 | const Backtrace* backtrace_; |
| 224 | const char* typeName1_; |
| 225 | const char* typeName2_; |
| 226 | }; |
| 227 | |
| 228 | /** |
| 229 | * A functor that determines which of two BacktraceMap entries |
| 230 | * has a higher count. |
| 231 | */ |
| 232 | class CountGreater { |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 233 | public: |
| 234 | bool operator()(std::pair<Key, size_t> bt1, std::pair<Key, size_t> bt2) const { |
David Reiss | c3b3622 | 2010-10-06 17:10:10 +0000 | [diff] [blame] | 235 | return bt1.second > bt2.second; |
| 236 | } |
| 237 | }; |
| 238 | |
| 239 | typedef __gnu_cxx::hash_map<Key, size_t, Key::Hash> BacktraceMap; |
| 240 | |
| 241 | /** |
| 242 | * A map describing how many times T_VIRTUAL_CALL() has been invoked. |
| 243 | */ |
| 244 | BacktraceMap virtual_calls; |
| 245 | Mutex virtual_calls_mutex; |
| 246 | |
| 247 | /** |
| 248 | * A map describing how many times T_GENERIC_PROTOCOL() has been invoked. |
| 249 | */ |
| 250 | BacktraceMap generic_calls; |
| 251 | Mutex generic_calls_mutex; |
| 252 | |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 253 | void _record_backtrace(BacktraceMap* map, const Mutex& mutex, Key* k) { |
David Reiss | c3b3622 | 2010-10-06 17:10:10 +0000 | [diff] [blame] | 254 | Guard guard(mutex); |
| 255 | |
| 256 | BacktraceMap::iterator it = map->find(*k); |
| 257 | if (it == map->end()) { |
| 258 | k->makePersistent(); |
| 259 | map->insert(std::make_pair(*k, 1)); |
| 260 | } else { |
| 261 | // increment the count |
| 262 | // NOTE: we could assert if it->second is 0 afterwards, since that would |
| 263 | // mean we've wrapped. |
| 264 | ++(it->second); |
| 265 | } |
| 266 | } |
| 267 | |
| 268 | /** |
| 269 | * Record an unnecessary virtual function call. |
| 270 | * |
| 271 | * This method is invoked by the T_VIRTUAL_CALL() macro. |
| 272 | */ |
| 273 | void profile_virtual_call(const std::type_info& type) { |
| 274 | int const skip = 1; // ignore this frame |
| 275 | Backtrace bt(skip); |
| 276 | Key k(&bt, type); |
| 277 | _record_backtrace(&virtual_calls, virtual_calls_mutex, &k); |
| 278 | } |
| 279 | |
| 280 | /** |
| 281 | * Record a call to a template processor with a protocol that is not the one |
| 282 | * specified in the template parameter. |
| 283 | * |
| 284 | * This method is invoked by the T_GENERIC_PROTOCOL() macro. |
| 285 | */ |
| 286 | void profile_generic_protocol(const std::type_info& template_type, |
| 287 | const std::type_info& prot_type) { |
| 288 | int const skip = 1; // ignore this frame |
| 289 | Backtrace bt(skip); |
| 290 | Key k(&bt, template_type, prot_type); |
| 291 | _record_backtrace(&generic_calls, generic_calls_mutex, &k); |
| 292 | } |
| 293 | |
| 294 | /** |
| 295 | * Print the recorded profiling information to the specified file. |
| 296 | */ |
| 297 | void profile_print_info(FILE* f) { |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 298 | typedef std::vector<std::pair<Key, size_t> > BacktraceVector; |
David Reiss | c3b3622 | 2010-10-06 17:10:10 +0000 | [diff] [blame] | 299 | |
| 300 | CountGreater is_greater; |
| 301 | |
| 302 | // Grab both locks for the duration of the print operation, |
| 303 | // to ensure the output is a consistent snapshot of a single point in time |
| 304 | Guard generic_calls_guard(generic_calls_mutex); |
| 305 | Guard virtual_calls_guard(virtual_calls_mutex); |
| 306 | |
| 307 | // print the info from generic_calls, sorted by frequency |
| 308 | // |
| 309 | // We print the generic_calls info ahead of virtual_calls, since it is more |
| 310 | // useful in some cases. All T_GENERIC_PROTOCOL calls can be eliminated |
| 311 | // from most programs. Not all T_VIRTUAL_CALLs will be eliminated by |
| 312 | // converting to templates. |
| 313 | BacktraceVector gp_sorted(generic_calls.begin(), generic_calls.end()); |
| 314 | std::sort(gp_sorted.begin(), gp_sorted.end(), is_greater); |
| 315 | |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 316 | for (BacktraceVector::const_iterator it = gp_sorted.begin(); it != gp_sorted.end(); ++it) { |
| 317 | Key const& key = it->first; |
David Reiss | c3b3622 | 2010-10-06 17:10:10 +0000 | [diff] [blame] | 318 | size_t const count = it->second; |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 319 | fprintf(f, |
| 320 | "T_GENERIC_PROTOCOL: %zu calls to %s with a %s:\n", |
| 321 | count, |
| 322 | key.getTypeName(), |
| 323 | key.getTypeName2()); |
David Reiss | c3b3622 | 2010-10-06 17:10:10 +0000 | [diff] [blame] | 324 | key.getBacktrace()->print(f, 2); |
| 325 | fprintf(f, "\n"); |
| 326 | } |
| 327 | |
| 328 | // print the info from virtual_calls, sorted by frequency |
| 329 | BacktraceVector vc_sorted(virtual_calls.begin(), virtual_calls.end()); |
| 330 | std::sort(vc_sorted.begin(), vc_sorted.end(), is_greater); |
| 331 | |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 332 | for (BacktraceVector::const_iterator it = vc_sorted.begin(); it != vc_sorted.end(); ++it) { |
| 333 | Key const& key = it->first; |
David Reiss | c3b3622 | 2010-10-06 17:10:10 +0000 | [diff] [blame] | 334 | size_t const count = it->second; |
| 335 | fprintf(f, "T_VIRTUAL_CALL: %zu calls on %s:\n", count, key.getTypeName()); |
| 336 | key.getBacktrace()->print(f, 2); |
| 337 | fprintf(f, "\n"); |
| 338 | } |
| 339 | } |
| 340 | |
| 341 | /** |
| 342 | * Print the recorded profiling information to stdout. |
| 343 | */ |
| 344 | void profile_print_info() { |
| 345 | profile_print_info(stdout); |
| 346 | } |
| 347 | |
| 348 | /** |
| 349 | * Write a BacktraceMap as Google CPU profiler binary data. |
| 350 | */ |
| 351 | static void profile_write_pprof_file(FILE* f, BacktraceMap const& map) { |
| 352 | // Write the header |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 353 | uintptr_t header[5] = {0, 3, 0, 0, 0}; |
David Reiss | c3b3622 | 2010-10-06 17:10:10 +0000 | [diff] [blame] | 354 | fwrite(&header, sizeof(header), 1, f); |
| 355 | |
| 356 | // Write the profile records |
| 357 | for (BacktraceMap::const_iterator it = map.begin(); it != map.end(); ++it) { |
| 358 | uintptr_t count = it->second; |
| 359 | fwrite(&count, sizeof(count), 1, f); |
| 360 | |
| 361 | Backtrace const* bt = it->first.getBacktrace(); |
| 362 | uintptr_t num_pcs = bt->getDepth(); |
| 363 | fwrite(&num_pcs, sizeof(num_pcs), 1, f); |
| 364 | |
| 365 | for (uintptr_t n = 0; n < num_pcs; ++n) { |
| 366 | void* pc = bt->getFrame(n); |
| 367 | fwrite(&pc, sizeof(pc), 1, f); |
| 368 | } |
| 369 | } |
| 370 | |
| 371 | // Write the trailer |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 372 | uintptr_t trailer[3] = {0, 1, 0}; |
David Reiss | c3b3622 | 2010-10-06 17:10:10 +0000 | [diff] [blame] | 373 | fwrite(&trailer, sizeof(trailer), 1, f); |
| 374 | |
| 375 | // Write /proc/self/maps |
| 376 | // TODO(simpkins): This only works on linux |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 377 | FILE* proc_maps = fopen("/proc/self/maps", "r"); |
David Reiss | c3b3622 | 2010-10-06 17:10:10 +0000 | [diff] [blame] | 378 | if (proc_maps) { |
| 379 | uint8_t buf[4096]; |
| 380 | while (true) { |
| 381 | size_t bytes_read = fread(buf, 1, sizeof(buf), proc_maps); |
| 382 | if (bytes_read == 0) { |
| 383 | break; |
| 384 | } |
| 385 | fwrite(buf, 1, bytes_read, f); |
| 386 | } |
| 387 | fclose(proc_maps); |
| 388 | } |
| 389 | } |
| 390 | |
| 391 | /** |
| 392 | * Write the recorded profiling information as pprof files. |
| 393 | * |
| 394 | * This writes the information using the Google CPU profiler binary data |
| 395 | * format, so it can be analyzed with pprof. Note that information about the |
| 396 | * protocol/transport data types cannot be stored in this file format. |
| 397 | * |
| 398 | * See http://code.google.com/p/google-perftools/ for more details. |
| 399 | * |
| 400 | * @param gen_calls_f The information about calls to |
| 401 | * profile_generic_protocol() will be written to this |
| 402 | * file. |
| 403 | * @param virtual_calls_f The information about calls to |
| 404 | * profile_virtual_call() will be written to this file. |
| 405 | */ |
| 406 | void profile_write_pprof(FILE* gen_calls_f, FILE* virtual_calls_f) { |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 407 | typedef std::vector<std::pair<Key, size_t> > BacktraceVector; |
David Reiss | c3b3622 | 2010-10-06 17:10:10 +0000 | [diff] [blame] | 408 | |
| 409 | CountGreater is_greater; |
| 410 | |
| 411 | // Grab both locks for the duration of the print operation, |
| 412 | // to ensure the output is a consistent snapshot of a single point in time |
| 413 | Guard generic_calls_guard(generic_calls_mutex); |
| 414 | Guard virtual_calls_guard(virtual_calls_mutex); |
| 415 | |
| 416 | // write the info from generic_calls |
| 417 | profile_write_pprof_file(gen_calls_f, generic_calls); |
| 418 | |
| 419 | // write the info from virtual_calls |
| 420 | profile_write_pprof_file(virtual_calls_f, virtual_calls); |
| 421 | } |
Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 422 | } |
| 423 | } // apache::thrift |
David Reiss | c3b3622 | 2010-10-06 17:10:10 +0000 | [diff] [blame] | 424 | |
| 425 | #endif // T_GLOBAL_PROFILE_VIRTUAL > 0 |