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