Implementing callback functionality for exception routines
[folly.git] / folly / experimental / exception_tracer / ExceptionCounterLib.cpp
1 /*
2  * Copyright 2016 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <folly/experimental/exception_tracer/ExceptionCounterLib.h>
18
19 #include <iosfwd>
20 #include <unordered_map>
21
22 #include <folly/RWSpinLock.h>
23 #include <folly/Synchronized.h>
24 #include <folly/ThreadLocal.h>
25
26 #include <folly/experimental/exception_tracer/ExceptionTracerLib.h>
27 #include <folly/experimental/exception_tracer/StackTrace.h>
28 #include <folly/experimental/symbolizer/Symbolizer.h>
29
30 using namespace folly::exception_tracer;
31
32 namespace {
33
34 // We are using hash of the stack trace to uniquely identify the exception
35 using ExceptionId = uintptr_t;
36
37 using ExceptionStatsHolderType =
38     std::unordered_map<ExceptionId, ExceptionStats>;
39
40 struct ExceptionStatsStorage {
41   void appendTo(ExceptionStatsHolderType& data) {
42     ExceptionStatsHolderType tempHolder;
43     SYNCHRONIZED(statsHolder) {
44       using std::swap;
45       swap(statsHolder, tempHolder);
46     }
47
48     for (const auto& myData : tempHolder) {
49       const auto& myStat = myData.second;
50
51       auto it = data.find(myData.first);
52       if (it != data.end()) {
53         it->second.count += myStat.count;
54       } else {
55         data.insert(myData);
56       }
57     }
58   }
59
60   folly::Synchronized<ExceptionStatsHolderType, folly::RWSpinLock> statsHolder;
61 };
62
63 class Tag {};
64
65 folly::ThreadLocal<ExceptionStatsStorage, Tag> gExceptionStats;
66
67 } // namespace
68
69 namespace folly {
70 namespace exception_tracer {
71
72 std::vector<ExceptionStats> getExceptionStatistics() {
73   ExceptionStatsHolderType accumulator;
74   for (auto& threadStats : gExceptionStats.accessAllThreads()) {
75     threadStats.appendTo(accumulator);
76   }
77
78   std::vector<ExceptionStats> result;
79   result.reserve(accumulator.size());
80   for (const auto& item : accumulator) {
81     result.push_back(item.second);
82   }
83
84   std::sort(result.begin(),
85             result.end(),
86             [](const ExceptionStats& lhs, const ExceptionStats& rhs) {
87               return (lhs.count > rhs.count);
88             });
89
90   return result;
91 }
92
93 std::ostream& operator<<(std::ostream& out, const ExceptionStats& stats) {
94   out << "Exception report: " << std::endl;
95   out << "Exception count: " << stats.count << std::endl;
96   out << stats.info;
97
98   return out;
99 }
100
101 } // namespace exception_tracer
102 } // namespace folly
103
104 namespace {
105
106 /*
107  * This handler gathers statistics on all exceptions thrown by the program
108  * Information is being stored in thread local storage.
109  */
110 void throwHandler(void*, std::type_info* exType, void (*)(void*)) noexcept {
111   ExceptionInfo info;
112   info.type = exType;
113   auto& frames = info.frames;
114
115   frames.resize(kMaxFrames);
116   auto n = folly::symbolizer::getStackTrace(frames.data(), kMaxFrames);
117
118   if (n == -1) {
119     LOG(ERROR) << "Invalid stack frame";
120     return;
121   }
122
123   frames.resize(n);
124   auto exceptionId = folly::hash::hash_range(frames.begin(), frames.end());
125
126   SYNCHRONIZED(holder, gExceptionStats->statsHolder) {
127     auto it = holder.find(exceptionId);
128     if (it != holder.end()) {
129       ++it->second.count;
130     } else {
131       holder.emplace(exceptionId, ExceptionStats{1, std::move(info)});
132     }
133   }
134 }
135
136 struct Initializer {
137   Initializer() { registerCxaThrowCallback(throwHandler); }
138 };
139
140 Initializer initializer;
141
142 } // namespace