1*65c59e02SInna Palant /*
2*65c59e02SInna Palant * Copyright (c) Facebook, Inc. and its affiliates.
3*65c59e02SInna Palant *
4*65c59e02SInna Palant * Licensed under the Apache License, Version 2.0 (the "License");
5*65c59e02SInna Palant * you may not use this file except in compliance with the License.
6*65c59e02SInna Palant * You may obtain a copy of the License at
7*65c59e02SInna Palant *
8*65c59e02SInna Palant * http://www.apache.org/licenses/LICENSE-2.0
9*65c59e02SInna Palant *
10*65c59e02SInna Palant * Unless required by applicable law or agreed to in writing, software
11*65c59e02SInna Palant * distributed under the License is distributed on an "AS IS" BASIS,
12*65c59e02SInna Palant * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*65c59e02SInna Palant * See the License for the specific language governing permissions and
14*65c59e02SInna Palant * limitations under the License.
15*65c59e02SInna Palant */
16*65c59e02SInna Palant
17*65c59e02SInna Palant #include <lyra/lyra.h>
18*65c59e02SInna Palant
19*65c59e02SInna Palant #include <atomic>
20*65c59e02SInna Palant #include <iomanip>
21*65c59e02SInna Palant #include <ios>
22*65c59e02SInna Palant #include <memory>
23*65c59e02SInna Palant #include <ostream>
24*65c59e02SInna Palant #include <vector>
25*65c59e02SInna Palant
26*65c59e02SInna Palant #ifndef _WIN32
27*65c59e02SInna Palant #include <dlfcn.h>
28*65c59e02SInna Palant #include <unwind.h>
29*65c59e02SInna Palant #endif
30*65c59e02SInna Palant
31*65c59e02SInna Palant #include <fbjni/detail/Log.h>
32*65c59e02SInna Palant
33*65c59e02SInna Palant using namespace std;
34*65c59e02SInna Palant
35*65c59e02SInna Palant namespace facebook {
36*65c59e02SInna Palant namespace lyra {
37*65c59e02SInna Palant
38*65c59e02SInna Palant namespace {
39*65c59e02SInna Palant
40*65c59e02SInna Palant class IosFlagsSaver {
41*65c59e02SInna Palant ios_base& ios_;
42*65c59e02SInna Palant ios_base::fmtflags flags_;
43*65c59e02SInna Palant
44*65c59e02SInna Palant public:
IosFlagsSaver(ios_base & ios)45*65c59e02SInna Palant IosFlagsSaver(ios_base& ios) : ios_(ios), flags_(ios.flags()) {}
46*65c59e02SInna Palant
~IosFlagsSaver()47*65c59e02SInna Palant ~IosFlagsSaver() {
48*65c59e02SInna Palant ios_.flags(flags_);
49*65c59e02SInna Palant }
50*65c59e02SInna Palant };
51*65c59e02SInna Palant
52*65c59e02SInna Palant struct BacktraceState {
53*65c59e02SInna Palant size_t skip;
54*65c59e02SInna Palant vector<InstructionPointer>& stackTrace;
55*65c59e02SInna Palant };
56*65c59e02SInna Palant
57*65c59e02SInna Palant #ifndef _MSC_VER
unwindCallback(struct _Unwind_Context * context,void * arg)58*65c59e02SInna Palant _Unwind_Reason_Code unwindCallback(struct _Unwind_Context* context, void* arg) {
59*65c59e02SInna Palant BacktraceState* state = reinterpret_cast<BacktraceState*>(arg);
60*65c59e02SInna Palant auto absoluteProgramCounter =
61*65c59e02SInna Palant reinterpret_cast<InstructionPointer>(_Unwind_GetIP(context));
62*65c59e02SInna Palant
63*65c59e02SInna Palant if (state->skip > 0) {
64*65c59e02SInna Palant --state->skip;
65*65c59e02SInna Palant return _URC_NO_REASON;
66*65c59e02SInna Palant }
67*65c59e02SInna Palant
68*65c59e02SInna Palant if (state->stackTrace.size() == state->stackTrace.capacity()) {
69*65c59e02SInna Palant return _URC_END_OF_STACK;
70*65c59e02SInna Palant }
71*65c59e02SInna Palant
72*65c59e02SInna Palant state->stackTrace.push_back(absoluteProgramCounter);
73*65c59e02SInna Palant
74*65c59e02SInna Palant return _URC_NO_REASON;
75*65c59e02SInna Palant }
76*65c59e02SInna Palant #endif
77*65c59e02SInna Palant
captureBacktrace(size_t skip,vector<InstructionPointer> & stackTrace)78*65c59e02SInna Palant void captureBacktrace(size_t skip, vector<InstructionPointer>& stackTrace) {
79*65c59e02SInna Palant // Beware of a bug on some platforms, which makes the trace loop until the
80*65c59e02SInna Palant // buffer is full when it reaches a noexcept function. It seems to be fixed in
81*65c59e02SInna Palant // newer versions of gcc. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56846
82*65c59e02SInna Palant // TODO(t10738439): Investigate workaround for the stack trace bug
83*65c59e02SInna Palant BacktraceState state = {skip, stackTrace};
84*65c59e02SInna Palant #ifndef _WIN32
85*65c59e02SInna Palant _Unwind_Backtrace(unwindCallback, &state);
86*65c59e02SInna Palant #endif
87*65c59e02SInna Palant }
88*65c59e02SInna Palant
89*65c59e02SInna Palant // this is a pointer to a function
90*65c59e02SInna Palant std::atomic<LibraryIdentifierFunctionType> gLibraryIdentifierFunction{nullptr};
91*65c59e02SInna Palant
92*65c59e02SInna Palant } // namespace
93*65c59e02SInna Palant
setLibraryIdentifierFunction(LibraryIdentifierFunctionType func)94*65c59e02SInna Palant void setLibraryIdentifierFunction(LibraryIdentifierFunctionType func) {
95*65c59e02SInna Palant gLibraryIdentifierFunction.store(func, std::memory_order_relaxed);
96*65c59e02SInna Palant }
97*65c59e02SInna Palant
buildId() const98*65c59e02SInna Palant std::string StackTraceElement::buildId() const {
99*65c59e02SInna Palant if (!hasBuildId_) {
100*65c59e02SInna Palant auto getBuildId =
101*65c59e02SInna Palant gLibraryIdentifierFunction.load(std::memory_order_relaxed);
102*65c59e02SInna Palant if (getBuildId) {
103*65c59e02SInna Palant buildId_ = getBuildId(libraryName());
104*65c59e02SInna Palant } else {
105*65c59e02SInna Palant buildId_ = "<unimplemented>";
106*65c59e02SInna Palant }
107*65c59e02SInna Palant hasBuildId_ = true;
108*65c59e02SInna Palant }
109*65c59e02SInna Palant return buildId_;
110*65c59e02SInna Palant }
111*65c59e02SInna Palant
getStackTrace(vector<InstructionPointer> & stackTrace,size_t skip)112*65c59e02SInna Palant void getStackTrace(vector<InstructionPointer>& stackTrace, size_t skip) {
113*65c59e02SInna Palant stackTrace.clear();
114*65c59e02SInna Palant captureBacktrace(skip + 1, stackTrace);
115*65c59e02SInna Palant }
116*65c59e02SInna Palant
117*65c59e02SInna Palant // TODO(t10737622): Improve on-device symbolification
getStackTraceSymbols(vector<StackTraceElement> & symbols,const vector<InstructionPointer> & trace)118*65c59e02SInna Palant void getStackTraceSymbols(
119*65c59e02SInna Palant vector<StackTraceElement>& symbols,
120*65c59e02SInna Palant const vector<InstructionPointer>& trace) {
121*65c59e02SInna Palant symbols.clear();
122*65c59e02SInna Palant symbols.reserve(trace.size());
123*65c59e02SInna Palant
124*65c59e02SInna Palant #ifndef _WIN32
125*65c59e02SInna Palant for (size_t i = 0; i < trace.size(); ++i) {
126*65c59e02SInna Palant Dl_info info;
127*65c59e02SInna Palant if (dladdr(trace[i], &info)) {
128*65c59e02SInna Palant symbols.emplace_back(
129*65c59e02SInna Palant trace[i],
130*65c59e02SInna Palant info.dli_fbase,
131*65c59e02SInna Palant info.dli_saddr,
132*65c59e02SInna Palant info.dli_fname ? info.dli_fname : "",
133*65c59e02SInna Palant info.dli_sname ? info.dli_sname : "");
134*65c59e02SInna Palant }
135*65c59e02SInna Palant }
136*65c59e02SInna Palant #endif
137*65c59e02SInna Palant }
138*65c59e02SInna Palant
operator <<(ostream & out,const StackTraceElement & elm)139*65c59e02SInna Palant ostream& operator<<(ostream& out, const StackTraceElement& elm) {
140*65c59e02SInna Palant IosFlagsSaver flags{out};
141*65c59e02SInna Palant
142*65c59e02SInna Palant out << "{dso=" << elm.libraryName() << " offset=" << hex << showbase
143*65c59e02SInna Palant << elm.libraryOffset();
144*65c59e02SInna Palant
145*65c59e02SInna Palant if (!elm.functionName().empty()) {
146*65c59e02SInna Palant out << " func=" << elm.functionName() << "()+" << elm.functionOffset();
147*65c59e02SInna Palant }
148*65c59e02SInna Palant
149*65c59e02SInna Palant out << " build-id=" << hex << setw(8) << elm.buildId() << "}";
150*65c59e02SInna Palant
151*65c59e02SInna Palant return out;
152*65c59e02SInna Palant }
153*65c59e02SInna Palant
154*65c59e02SInna Palant // TODO(t10737667): The implement a tool that parse the stack trace and
155*65c59e02SInna Palant // symbolicate it
operator <<(ostream & out,const vector<StackTraceElement> & trace)156*65c59e02SInna Palant ostream& operator<<(ostream& out, const vector<StackTraceElement>& trace) {
157*65c59e02SInna Palant IosFlagsSaver flags{out};
158*65c59e02SInna Palant
159*65c59e02SInna Palant auto i = 0;
160*65c59e02SInna Palant out << "Backtrace:\n";
161*65c59e02SInna Palant for (auto& elm : trace) {
162*65c59e02SInna Palant out << " #" << dec << setfill('0') << setw(2) << i++ << " " << elm
163*65c59e02SInna Palant << '\n';
164*65c59e02SInna Palant }
165*65c59e02SInna Palant
166*65c59e02SInna Palant return out;
167*65c59e02SInna Palant }
168*65c59e02SInna Palant
logStackTrace(const vector<StackTraceElement> & trace)169*65c59e02SInna Palant void logStackTrace(const vector<StackTraceElement>& trace) {
170*65c59e02SInna Palant auto i = 0;
171*65c59e02SInna Palant FBJNI_LOGE("Backtrace:");
172*65c59e02SInna Palant for (auto& elm : trace) {
173*65c59e02SInna Palant if (!elm.functionName().empty()) {
174*65c59e02SInna Palant FBJNI_LOGE(
175*65c59e02SInna Palant " #%02d |lyra|{dso=%s offset=%#tx func=%s+%#x build-id=%s}",
176*65c59e02SInna Palant i++,
177*65c59e02SInna Palant elm.libraryName().c_str(),
178*65c59e02SInna Palant elm.libraryOffset(),
179*65c59e02SInna Palant elm.functionName().c_str(),
180*65c59e02SInna Palant elm.functionOffset(),
181*65c59e02SInna Palant elm.buildId().c_str());
182*65c59e02SInna Palant } else {
183*65c59e02SInna Palant FBJNI_LOGE(
184*65c59e02SInna Palant " #%02d |lyra|{dso=%s offset=%#tx build-id=%s}",
185*65c59e02SInna Palant i++,
186*65c59e02SInna Palant elm.libraryName().c_str(),
187*65c59e02SInna Palant elm.libraryOffset(),
188*65c59e02SInna Palant elm.buildId().c_str());
189*65c59e02SInna Palant }
190*65c59e02SInna Palant }
191*65c59e02SInna Palant }
192*65c59e02SInna Palant
193*65c59e02SInna Palant } // namespace lyra
194*65c59e02SInna Palant } // namespace facebook
195