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