1 // =================================
  2 // Copyright (c) 2021 Seppo Laakko
  3 // Distributed under the MIT license
  4 // =================================
  5 
  6 #include <cmajor/rt/Unwind.hpp>
  7 #include <memory>
  8 #include <string>
  9 #include <unordered_map>
 10 #include <vector>
 11 
 12 #ifdef _WIN32
 13 
 14 #else
 15          void* unwindList = nullptr;
 16 #endif
 17 
 18 struct UnwindInfo 
 19 {
 20     UnwindInfo* next;
 21     void* function;
 22     int32_t line;
 23 };
 24 
 25 void* RtPushUnwindInfo(void* unwindInfo)
 26 {
 27     void* prevUnwindInfo = unwindList;
 28     unwindList = unwindInfo;
 29     return prevUnwindInfo;
 30 }
 31 
 32 void RtPopUnwindInfo(void* prevUnwindInfo)
 33 {
 34     unwindList = prevUnwindInfo;
 35 }
 36 
 37 struct FunctionUnwindInfo 
 38 {
 39     FunctionUnwindInfo(const char* functionName_const char* sourceFilePath_) : functionName(functionName_)sourceFilePath(sourceFilePath_) {}
 40     std::string functionName;
 41     std::string sourceFilePath;
 42 };
 43 
 44 class GlobalUnwindInfo 
 45 {
 46 public:
 47     static void Init();
 48     static void Done();
 49     static bool Initialized() { return instance != nullptr; }
 50     static GlobalUnwindInfo& Instance() { return *instance; }
 51     void AddUnwindInfo(void* functionAddressconst char* functionNameconst char* sourceFilePath);
 52     const char* GetCallStack(UnwindInfo* unwindInfoList);
 53     void DisposeCallStack(UnwindInfo* unwindInfoList);
 54 private:
 55     static std::unique_ptr<GlobalUnwindInfo> instance;
 56     GlobalUnwindInfo();
 57     std::unordered_map<void*FunctionUnwindInfo*> unwindInfoMap;
 58     std::vector<std::std::unique_ptr<FunctionUnwindInfo>>unwindInfoVec;
 59     std::unordered_map<void*std::string*> callStackMap;
 60 };
 61 
 62 void GlobalUnwindInfo::Init()
 63 {
 64     instance.reset(new GlobalUnwindInfo());
 65 }
 66 
 67 void GlobalUnwindInfo::Done()
 68 {
 69     instance.reset();
 70 }
 71 
 72 std::unique_ptr<GlobalUnwindInfo> GlobalUnwindInfo::instance;
 73 
 74 GlobalUnwindInfo::GlobalUnwindInfo()
 75 {
 76 }
 77 
 78 void GlobalUnwindInfo::AddUnwindInfo(void* functionAddressconst char* functionNameconst char* sourceFilePath)
 79 {
 80     auto it = unwindInfoMap.find(functionAddress);
 81     if (it == unwindInfoMap.cend())
 82     {
 83         FunctionUnwindInfo* unwindInfo = new FunctionUnwindInfo(functionNamesourceFilePath);
 84         unwindInfoVec.push_back(std::unique_ptr<FunctionUnwindInfo>(unwindInfo));
 85         unwindInfoMap[functionAddress] = unwindInfo;
 86     }
 87 }
 88 
 89 const char* GlobalUnwindInfo::GetCallStack(UnwindInfo* unwindInfoList)
 90 {
 91     if (!unwindInfoList) return "";
 92     auto it = callStackMap.find(unwindInfoList);
 93     if (it != callStackMap.cend())
 94     {
 95         return it->second->c_str();
 96     }
 97     std::unique_ptr<std::string> callStack(new std::string());
 98     UnwindInfo* unwindInfo = unwindInfoList;
 99     while (unwindInfo)
100     {
101         void* function = unwindInfo->function;
102         auto it = unwindInfoMap.find(function);
103         if (it != unwindInfoMap.cend())
104         {
105             FunctionUnwindInfo* functionUnwindInfo = it->second;
106             callStack->append(functionUnwindInfo->functionName).append(1' ').append(functionUnwindInfo->sourceFilePath);
107             if (unwindInfo->line != 0 && unwindInfo->line != -1)
108             {
109                 callStack->append(1':').append(std::to_string(unwindInfo->line));
110             }
111             callStack->append("\n");
112         }
113         unwindInfo = unwindInfo->next;
114     }
115     const char* callStackStr = callStack->c_str();
116     callStackMap[unwindInfoList] = callStack.release();
117     return callStackStr;
118 }
119 
120 void GlobalUnwindInfo::DisposeCallStack(UnwindInfo* unwindInfoList)
121 {
122     auto it = callStackMap.find(unwindInfoList);
123     if (it != callStackMap.cend())
124     {
125         delete it->second;
126         callStackMap.erase(unwindInfoList);
127     }
128 }
129 
130 void RtAddCompileUnitFunction(void* functionAddressconst char* functionNameconst char* sourceFilePath)
131 {
132     if (GlobalUnwindInfo::Initialized())
133     {
134         GlobalUnwindInfo::Instance().AddUnwindInfo(functionAddressfunctionNamesourceFilePath);
135     }
136 }
137 
138 const char* RtGetCallStack()
139 {
140     if (GlobalUnwindInfo::Initialized())
141     {
142         return GlobalUnwindInfo::Instance().GetCallStack(static_cast<UnwindInfo*>(unwindList));
143     }
144     return "";
145 }
146 
147 void RtDisposeCallStack()
148 {
149     if (GlobalUnwindInfo::Initialized())
150     {
151         GlobalUnwindInfo::Instance().DisposeCallStack(static_cast<UnwindInfo*>(unwindList));
152     }
153 }
154 
155 void InitUnwind()
156 {
157     GlobalUnwindInfo::Init();
158 }
159 
160 void DoneUnwind()
161 {
162     GlobalUnwindInfo::Done();
163 }