/src/WasmEdge/lib/system/stacktrace.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | // SPDX-License-Identifier: Apache-2.0 |
2 | | // SPDX-FileCopyrightText: 2019-2024 Second State INC |
3 | | |
4 | | #include "system/stacktrace.h" |
5 | | #include "common/spdlog.h" |
6 | | #include <fmt/ranges.h> |
7 | | |
8 | | #if WASMEDGE_OS_WINDOWS |
9 | | #include "system/winapi.h" |
10 | | #elif WASMEDGE_OS_LINUX |
11 | | #include <unwind.h> |
12 | | #elif WASMEDGE_OS_MACOS |
13 | | #include <execinfo.h> |
14 | | #endif |
15 | | |
16 | | namespace WasmEdge { |
17 | | |
18 | | using namespace std::literals; |
19 | | |
20 | 0 | Span<void *const> stackTrace(Span<void *> Buffer) noexcept { |
21 | | #if WASMEDGE_OS_WINDOWS |
22 | | struct DbgHelp { |
23 | | DbgHelp() noexcept : Process(winapi::GetCurrentProcess()) { |
24 | | winapi::SymSetOptions(winapi::SYMOPT_DEFERRED_LOADS_); |
25 | | winapi::SymInitializeW(Process, nullptr, true); |
26 | | SelfBase = winapi::SymGetModuleBase64( |
27 | | Process, reinterpret_cast<winapi::DWORD64_>(&stackTrace)); |
28 | | NtDllBase = winapi::SymGetModuleBase64( |
29 | | Process, reinterpret_cast<winapi::DWORD64_>( |
30 | | &winapi::RtlCaptureStackBackTrace)); |
31 | | Kernel32Base = winapi::SymGetModuleBase64( |
32 | | Process, reinterpret_cast<winapi::DWORD64_>(&winapi::CloseHandle)); |
33 | | } |
34 | | ~DbgHelp() noexcept { winapi::SymCleanup(Process); } |
35 | | void refresh() noexcept { winapi::SymRefreshModuleList(Process); } |
36 | | winapi::HANDLE_ Process; |
37 | | winapi::DWORD64_ SelfBase, NtDllBase, Kernel32Base; |
38 | | }; |
39 | | static DbgHelp Helper; |
40 | | Helper.refresh(); |
41 | | auto Depth = static_cast<size_t>(winapi::RtlCaptureStackBackTrace( |
42 | | 1u, static_cast<winapi::ULONG_>(Buffer.size()), Buffer.data(), nullptr)); |
43 | | size_t NewDepth = 0; |
44 | | for (size_t I = 0; I < Depth; ++I) { |
45 | | auto Base = winapi::SymGetModuleBase64( |
46 | | Helper.Process, reinterpret_cast<winapi::DWORD64_>(Buffer[I])); |
47 | | if (Base == 0 || (Base != Helper.SelfBase && Base != Helper.NtDllBase && |
48 | | Base != Helper.Kernel32Base)) { |
49 | | Buffer[NewDepth++] = Buffer[I]; |
50 | | } |
51 | | } |
52 | | return Buffer.first(static_cast<size_t>(NewDepth)); |
53 | | #elif WASMEDGE_OS_LINUX |
54 | | struct BacktraceState { |
55 | 0 | Span<void *> Buffer; |
56 | 0 | size_t Index; |
57 | 0 | }; |
58 | 0 | BacktraceState State{Buffer, 0}; |
59 | 0 | _Unwind_Backtrace( |
60 | 0 | [](struct _Unwind_Context *Ctx, void *Arg) noexcept { |
61 | 0 | auto &State = *static_cast<BacktraceState *>(Arg); |
62 | 0 | if (State.Index >= State.Buffer.size()) { |
63 | 0 | return _URC_END_OF_STACK; |
64 | 0 | } |
65 | 0 | State.Buffer[State.Index++] = |
66 | 0 | reinterpret_cast<void *>(_Unwind_GetIP(Ctx)); |
67 | 0 | return _URC_NO_REASON; |
68 | 0 | }, |
69 | 0 | &State); |
70 | 0 | return Buffer.first(State.Index); |
71 | | #elif WASMEDGE_OS_MACOS |
72 | | const auto Depth = backtrace(Buffer.data(), Buffer.size()); |
73 | | return Buffer.first(Depth); |
74 | | #endif |
75 | 0 | } |
76 | | |
77 | | Span<const uint32_t> |
78 | | interpreterStackTrace(const Runtime::StackManager &StackMgr, |
79 | 0 | Span<uint32_t> Buffer) noexcept { |
80 | 0 | size_t Index = 0; |
81 | 0 | if (auto Module = StackMgr.getModule()) { |
82 | 0 | const auto FuncInsts = Module->getFunctionInstances(); |
83 | 0 | std::map<AST::InstrView::iterator, int64_t> Funcs; |
84 | 0 | for (size_t I = 0; I < FuncInsts.size(); ++I) { |
85 | 0 | const auto &Func = FuncInsts[I]; |
86 | 0 | if (Func && Func->isWasmFunction()) { |
87 | 0 | const auto &Instrs = Func->getInstrs(); |
88 | 0 | Funcs.emplace(Instrs.end(), INT64_C(-1)); |
89 | 0 | Funcs.emplace(Instrs.begin(), I); |
90 | 0 | } |
91 | 0 | } |
92 | 0 | for (const auto &Frame : StackMgr.getFramesSpan()) { |
93 | 0 | auto Entry = Frame.From; |
94 | 0 | auto Iter = Funcs.lower_bound(Entry); |
95 | 0 | if ((Iter == Funcs.end() || Iter->first > Entry) && |
96 | 0 | Iter != Funcs.begin()) { |
97 | 0 | --Iter; |
98 | 0 | } |
99 | 0 | if (Iter != Funcs.end() && Iter->first < Entry && |
100 | 0 | Iter->second >= INT64_C(0) && Index < Buffer.size()) { |
101 | 0 | Buffer[Index++] = static_cast<uint32_t>(Iter->second); |
102 | 0 | } |
103 | 0 | } |
104 | 0 | } |
105 | 0 | return Buffer.first(Index); |
106 | 0 | } |
107 | | |
108 | | Span<const uint32_t> compiledStackTrace(const Runtime::StackManager &StackMgr, |
109 | 0 | Span<uint32_t> Buffer) noexcept { |
110 | 0 | std::array<void *, 256> StackTraceBuffer; |
111 | 0 | return compiledStackTrace(StackMgr, stackTrace(StackTraceBuffer), Buffer); |
112 | 0 | } |
113 | | |
114 | | Span<const uint32_t> compiledStackTrace(const Runtime::StackManager &StackMgr, |
115 | | Span<void *const> Stack, |
116 | 0 | Span<uint32_t> Buffer) noexcept { |
117 | 0 | std::map<void *, int64_t> Funcs; |
118 | 0 | size_t Index = 0; |
119 | 0 | if (auto Module = StackMgr.getModule()) { |
120 | 0 | const auto FuncInsts = Module->getFunctionInstances(); |
121 | 0 | for (size_t I = 0; I < FuncInsts.size(); ++I) { |
122 | 0 | const auto &Func = FuncInsts[I]; |
123 | 0 | if (Func && Func->isCompiledFunction()) { |
124 | 0 | Funcs.emplace( |
125 | 0 | reinterpret_cast<void *>(Func->getFuncType().getSymbol().get()), |
126 | 0 | INT64_C(-1)); |
127 | 0 | Funcs.emplace(Func->getSymbol().get(), I); |
128 | 0 | } |
129 | 0 | } |
130 | 0 | for (auto Entry : Stack) { |
131 | 0 | auto Iter = Funcs.lower_bound(Entry); |
132 | 0 | if ((Iter == Funcs.end() || Iter->first > Entry) && |
133 | 0 | Iter != Funcs.begin()) { |
134 | 0 | --Iter; |
135 | 0 | } |
136 | 0 | if (Iter != Funcs.end() && Iter->first < Entry && |
137 | 0 | Iter->second >= INT64_C(0) && Index < Buffer.size()) { |
138 | 0 | Buffer[Index++] = static_cast<uint32_t>(Iter->second); |
139 | 0 | } |
140 | 0 | } |
141 | 0 | } |
142 | 0 | return Buffer.first(Index); |
143 | 0 | } |
144 | | |
145 | 0 | void dumpStackTrace(Span<const uint32_t> Stack) noexcept { |
146 | 0 | spdlog::error("calling stack:{}"sv, fmt::join(Stack, ", "sv)); |
147 | 0 | } |
148 | | |
149 | | } // namespace WasmEdge |