xref: /aosp_15_r20/external/fbjni/cxx/lyra/lyra.cpp (revision 65c59e023c5336bbd4a23be7af78407e3d80e7e7)
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