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 
20 #include <thrift/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 #include <thrift/concurrency/Mutex.h>
39 
40 #include <ext/hash_map>
41 #include <execinfo.h>
42 #include <stdio.h>
43 
44 namespace apache {
45 namespace thrift {
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 {
56 public:
57   Backtrace(int skip = 0);
58   Backtrace(Backtrace const& bt);
59 
operator =(Backtrace const & bt)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 
operator ==(Backtrace const & bt) const67   bool operator==(Backtrace const& bt) const { return (cmp(bt) == 0); }
68 
hash() const69   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 
cmp(Backtrace const & bt) const77   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) {
84       int diff = reinterpret_cast<intptr_t>(callers_[n])
85                  - reinterpret_cast<intptr_t>(bt.callers_[n]);
86       if (diff != 0) {
87         return diff;
88       }
89     }
90 
91     return 0;
92   }
93 
print(FILE * f,int indent=0,int start=0) const94   void print(FILE* f, int indent = 0, int start = 0) const {
95     char** strings = backtrace_symbols(callers_, numCallers_);
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 
getDepth() const110   int getDepth() const { return numCallers_ - skip_; }
111 
getFrame(int index) const112   void* getFrame(int index) const {
113     int adjusted_index = index + skip_;
114     if (adjusted_index < 0 || adjusted_index >= numCallers_) {
115       return NULL;
116     }
117     return callers_[adjusted_index];
118   }
119 
120 private:
121   void* callers_[MAX_STACK_DEPTH];
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
Backtrace(int skip)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 
Backtrace(Backtrace const & bt)137 Backtrace::Backtrace(Backtrace const& bt) : numCallers_(bt.numCallers_), skip_(bt.skip_) {
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 {
147 public:
148   class Hash {
149   public:
operator ()(Key const & k) const150     size_t operator()(Key const& k) const { return k.hash(); }
151   };
152 
Key(const Backtrace * bt,const std::type_info & type_info)153   Key(const Backtrace* bt, const std::type_info& type_info)
154     : backtrace_(bt), typeName1_(type_info.name()), typeName2_(NULL) {}
155 
Key(const Backtrace * bt,const std::type_info & type_info1,const std::type_info & type_info2)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()) {}
158 
Key(const Key & k)159   Key(const Key& k)
160     : backtrace_(k.backtrace_), typeName1_(k.typeName1_), typeName2_(k.typeName2_) {}
161 
operator =(const Key & k)162   void operator=(const Key& k) {
163     backtrace_ = k.backtrace_;
164     typeName1_ = k.typeName1_;
165     typeName2_ = k.typeName2_;
166   }
167 
getBacktrace() const168   const Backtrace* getBacktrace() const { return backtrace_; }
169 
getTypeName() const170   const char* getTypeName() const { return typeName1_; }
171 
getTypeName2() const172   const char* getTypeName2() const { return typeName2_; }
173 
makePersistent()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    */
cleanup()190   void cleanup() {
191     delete backtrace_;
192     backtrace_ = NULL;
193   }
194 
cmp(const Key & k) const195   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 
operator ==(const Key & k) const212   bool operator==(const Key& k) const { return cmp(k) == 0; }
213 
hash() const214   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.
218     return backtrace_->hash() ^ reinterpret_cast<size_t>(typeName1_)
219            ^ reinterpret_cast<size_t>(typeName2_);
220   }
221 
222 private:
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 {
233 public:
operator ()(std::pair<Key,size_t> bt1,std::pair<Key,size_t> bt2) const234   bool operator()(std::pair<Key, size_t> bt1, std::pair<Key, size_t> bt2) const {
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 
_record_backtrace(BacktraceMap * map,const Mutex & mutex,Key * k)253 void _record_backtrace(BacktraceMap* map, const Mutex& mutex, Key* k) {
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  */
profile_virtual_call(const std::type_info & type)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  */
profile_generic_protocol(const std::type_info & template_type,const std::type_info & prot_type)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  */
profile_print_info(FILE * f)297 void profile_print_info(FILE* f) {
298   typedef std::vector<std::pair<Key, size_t> > BacktraceVector;
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 
316   for (BacktraceVector::const_iterator it = gp_sorted.begin(); it != gp_sorted.end(); ++it) {
317     Key const& key = it->first;
318     size_t const count = it->second;
319     fprintf(f,
320             "T_GENERIC_PROTOCOL: %zu calls to %s with a %s:\n",
321             count,
322             key.getTypeName(),
323             key.getTypeName2());
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 
332   for (BacktraceVector::const_iterator it = vc_sorted.begin(); it != vc_sorted.end(); ++it) {
333     Key const& key = it->first;
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  */
profile_print_info()344 void profile_print_info() {
345   profile_print_info(stdout);
346 }
347 
348 /**
349  * Write a BacktraceMap as Google CPU profiler binary data.
350  */
profile_write_pprof_file(FILE * f,BacktraceMap const & map)351 static void profile_write_pprof_file(FILE* f, BacktraceMap const& map) {
352   // Write the header
353   uintptr_t header[5] = {0, 3, 0, 0, 0};
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
372   uintptr_t trailer[3] = {0, 1, 0};
373   fwrite(&trailer, sizeof(trailer), 1, f);
374 
375   // Write /proc/self/maps
376   // TODO(simpkins): This only works on linux
377   FILE* proc_maps = fopen("/proc/self/maps", "r");
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  */
profile_write_pprof(FILE * gen_calls_f,FILE * virtual_calls_f)406 void profile_write_pprof(FILE* gen_calls_f, FILE* virtual_calls_f) {
407   typedef std::vector<std::pair<Key, size_t> > BacktraceVector;
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 }
422 }
423 } // apache::thrift
424 
425 #endif // T_GLOBAL_PROFILE_VIRTUAL > 0
426