#ifndef MRT_GC_DEBUGGER_H
#define MRT_GC_DEBUGGER_H
#include <algorithm>
#include <list>
#include <map>
#include <vector>
#include "Base/Log.h"
#include "Base/LogFile.h"
#include "Base/TimeUtils.h"
#include "LoaderManager.h"
#include "StackMap/StackMap.h"
#if defined(MRT_DEBUG) && (MRT_DEBUG == 1)
#define GCINFO_DEBUG (false)
#else
#define GCINFO_DEBUG (false)
#endif
namespace MapleRuntime {
#if GCINFO_DEBUG
class GCInfoNode {
public:
enum RootType {
REG_ROOT,
SLOT_ROOT,
};
static GCInfoNode BuildNodeForTrace(Uptr startIP, Uptr ip, FrameAddress* fa)
{
CString time = TimeUtil::GetTimestamp();
#ifdef __APPLE__
FuncDescRef funcDesc = MFuncDesc::GetFuncDesc(fa);
#else
FuncDescRef funcDesc = MFuncDesc::GetFuncDesc(startIP);
#endif
return GCInfoNode(funcDesc->GetFuncName(), time, startIP, ip, reinterpret_cast<Uptr>(fa->returnAddress));
}
GCInfoNode() = default;
GCInfoNode(const CString& name, const CString& time, Uptr startIp, Uptr ip, Uptr retPC)
: methodName(name), startPC(startIp), pc(ip), ra(retPC), timeStamp(time)
{}
virtual ~GCInfoNode() = default;
virtual void DumpFrameGCInfo() const
{
DLOG(ENUM, " time stamp: %s method name: %s, start ip: %p, frame pc: %p, return address: %p", timeStamp.Str(),
methodName.Str(), startPC, pc, ra);
DumpMapGCInfo(regRoots, "Register roots", "register: %d, root: %p ");
DumpMapGCInfo(slotRoots, "Slot roots", "offset: %d, root: %p ");
DumpMapGCInfo(invalidRegRoots, "Invalid register roots", "register id: %d, root: %p ");
DumpMapGCInfo(invalidSlotRoots, "Invalid slot roots", "offset: %d, root: %p ");
}
template<bool isValid = true>
void InsertRegRoot(RegisterNum reg, const BaseObject* ref)
{
if (isValid) {
regRoots[reg] = ref;
} else {
invalidRegRoots[reg] = ref;
}
}
template<bool isValid = true>
void InsertSlotRoots(SlotBias off, const BaseObject* ref)
{
if (isValid) {
slotRoots[off] = ref;
} else {
invalidSlotRoots[off] = ref;
}
}
protected:
template<class MapType>
static void DumpMapGCInfo(const MapType& rootMap, const CString& title, const CString& stringFormat)
{
constexpr size_t numPerRow = 5;
constexpr size_t defaultCount = 0;
DLOG(ENUM, " %s: {", title.Str());
size_t size = rootMap.size();
size_t remain = size % numPerRow;
size_t count = defaultCount;
CString detail = "";
for (const auto& x : rootMap) {
if (count == defaultCount) {
detail.Append(" ");
}
detail.Append(CString::FormatString(stringFormat.Str(), x.first, x.second));
if (++count == numPerRow) {
count = defaultCount;
detail.Append("\n");
}
}
if (remain != defaultCount) {
detail.Append("\n");
}
detail.Append(" }\n");
DLOG(ENUM, "%s", detail.Str());
}
private:
CString methodName;
Uptr startPC;
Uptr pc;
Uptr ra;
CString timeStamp;
std::map<RegisterNum, const BaseObject*> regRoots;
std::map<RegisterNum, const BaseObject*> invalidRegRoots;
std::map<SlotBias, const BaseObject*> slotRoots;
std::map<SlotBias, const BaseObject*> invalidSlotRoots;
};
class GCInfoNodeForFix : public GCInfoNode {
public:
static GCInfoNodeForFix BuildNodeForFix(Uptr startIP, Uptr ip, FrameAddress* fa)
{
CString time = TimeUtil::GetTimestamp();
#ifdef __APPLE__
FuncDescRef funcDesc = MFuncDesc::GetFuncDesc(fa);
#else
FuncDescRef funcDesc = MFuncDesc::GetFuncDesc(startIP);
#endif
return GCInfoNodeForFix(funcDesc->GetFuncName(), time, startIP, ip, reinterpret_cast<Uptr>(fa->returnAddress));
}
GCInfoNodeForFix() = default;
GCInfoNodeForFix(const CString& name, const CString& time, Uptr startIp, Uptr ip, Uptr retPC)
: GCInfoNode(name, time, startIp, ip, retPC)
{}
virtual ~GCInfoNodeForFix() = default;
void InsertDerivedPtrRef(BasePtrType base, DerivedPtrType derived) { derivedPtrRef[base] = derived; }
void DumpFrameGCInfo() const override
{
GCInfoNode::DumpFrameGCInfo();
DumpMapGCInfo(derivedPtrRef, "derived ptr references", "base object: %#zx, derived ptr: %#zx");
}
private:
std::map<BasePtrType, DerivedPtrType> derivedPtrRef;
};
class CurrentGCInfo {
public:
~CurrentGCInfo(){};
void PushFrameInfoForTrace(const GCInfoNode& frameGCInfo) { gcInfosForTrace.push_back(frameGCInfo); }
void PushFrameInfoForTrace(const GCInfoNode&& frameGCInfo) { gcInfosForTrace.push_back(frameGCInfo); }
void PushFrameInfoForFix(const GCInfoNodeForFix& frameGCInfo) { gcInfosForFix.push_back(frameGCInfo); }
void PushFrameInfoForFix(const GCInfoNodeForFix&& frameGCInfo) { gcInfosForFix.push_back(frameGCInfo); }
void Clear()
{
gcInfosForTrace.clear();
std::vector<GCInfoNode>().swap(gcInfosForTrace);
gcInfosForFix.clear();
std::vector<GCInfoNodeForFix>().swap(gcInfosForFix);
}
void DumpFrameInfo() const
{
DLOG(ENUM, " fix roots info:");
std::for_each(gcInfosForFix.begin(), gcInfosForFix.end(),
[](const GCInfoNodeForFix& info) { info.DumpFrameGCInfo(); });
DLOG(ENUM, " trace roots info:");
std::for_each(gcInfosForTrace.begin(), gcInfosForTrace.end(),
[](const GCInfoNode& info) { info.DumpFrameGCInfo(); });
}
private:
std::vector<GCInfoNode> gcInfosForTrace;
std::vector<GCInfoNodeForFix> gcInfosForFix;
};
class GCInfos {
public:
GCInfos() = default;
~GCInfos() = default;
void CreateCurrentGCInfo()
{
constexpr size_t numLimit = 10;
if (gcInfos.size() >= numLimit) {
auto& front = gcInfos.front();
front.Clear();
gcInfos.pop_front();
}
gcInfos.push_back(CurrentGCInfo());
}
void PushFrameInfoForTrace(const GCInfoNode& frameGCInfo) { GetCurrentGCInfo().PushFrameInfoForTrace(frameGCInfo); }
void PushFrameInfoForTrace(const GCInfoNode&& frameGCInfo)
{
GetCurrentGCInfo().PushFrameInfoForTrace(frameGCInfo);
}
void PushFrameInfoForFix(const GCInfoNodeForFix& infoNodeFoxFix)
{
GetCurrentGCInfo().PushFrameInfoForFix(infoNodeFoxFix);
}
void PushFrameInfoForFix(const GCInfoNodeForFix&& infoNodeForFix)
{
GetCurrentGCInfo().PushFrameInfoForFix(infoNodeForFix);
}
void DumpGCInfos() const
{
size_t size = gcInfos.size();
DLOG(ENUM, " current thread happened %d times GC", size);
size_t i = 1;
std::for_each(gcInfos.rbegin(), gcInfos.rend(), [&i](const CurrentGCInfo& cur) {
DLOG(ENUM, " the %d scan stack: ", i++);
cur.DumpFrameInfo();
});
}
private:
CurrentGCInfo& GetCurrentGCInfo()
{
if (UNLIKELY(gcInfos.empty())) {
CreateCurrentGCInfo();
}
return gcInfos.back();
}
std::list<CurrentGCInfo> gcInfos;
};
#endif
}
#endif