Coverage Report

Created: 2025-07-01 06:18

/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