1 /* 2 * Copyright (c) 2025 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 #ifndef COMMON_COMPONENTS_HEAP_COLLECTOR_GC_DEBUGGER_H 16 #define COMMON_COMPONENTS_HEAP_COLLECTOR_GC_DEBUGGER_H 17 18 #include <algorithm> 19 #include <list> 20 #include <map> 21 #include <vector> 22 23 #include "common_components/base/time_utils.h" 24 #include "common_components/log/log.h" 25 26 #ifndef NDEBUG 27 #define GCINFO_DEBUG (false) 28 #else 29 #define GCINFO_DEBUG (false) 30 #endif 31 32 namespace common { 33 #if defined(GCINFO_DEBUG) && GCINFO_DEBUG 34 class GCInfoNode { 35 public: 36 enum RootType { 37 REG_ROOT, 38 SLOT_ROOT, 39 }; BuildNodeForMarking(uintptr_t startIP,uintptr_t ip,FrameAddress * fa)40 static GCInfoNode BuildNodeForMarking(uintptr_t startIP, uintptr_t ip, FrameAddress* fa) 41 { 42 CString time = TimeUtil::GetTimestamp(); 43 44 #ifdef __APPLE__ 45 FuncDescRef funcDesc = MFuncDesc::GetFuncDesc(fa); 46 #else 47 FuncDescRef funcDesc = MFuncDesc::GetFuncDesc(startIP); 48 #endif 49 return GCInfoNode(funcDesc->GetFuncName(), time, startIP, ip, reinterpret_cast<uintptr_t>(fa->returnAddress)); 50 } 51 GCInfoNode() = default; GCInfoNode(const CString & name,const CString & time,uintptr_t startIp,uintptr_t ip,uintptr_t retPC)52 GCInfoNode(const CString& name, const CString& time, uintptr_t startIp, uintptr_t ip, uintptr_t retPC) 53 : methodName(name), startPC(startIp), pc(ip), ra(retPC), timeStamp(time) {} 54 virtual ~GCInfoNode() = default; DumpFrameGCInfo()55 virtual void DumpFrameGCInfo() const 56 { 57 DLOG(ENUM, " time stamp: %s method name: %s, start ip: %p, frame pc: %p, return address: %p", timeStamp.Str(), 58 methodName.Str(), startPC, pc, ra); 59 DumpMapGCInfo(regRoots, "Register roots", "register: %d, root: %p "); 60 DumpMapGCInfo(slotRoots, "Slot roots", "offset: %d, root: %p "); 61 DumpMapGCInfo(invalidRegRoots, "Invalid register roots", "register id: %d, root: %p "); 62 DumpMapGCInfo(invalidSlotRoots, "Invalid slot roots", "offset: %d, root: %p "); 63 } 64 template<bool isValid = true> InsertSlotRoots(SlotBias off,const BaseObject * ref)65 void InsertSlotRoots(SlotBias off, const BaseObject* ref) 66 { 67 if (isValid) { 68 slotRoots[off] = ref; 69 } else { 70 invalidSlotRoots[off] = ref; 71 } 72 } 73 template<bool isValid = true> InsertRegRoot(RegisterNum reg,const BaseObject * ref)74 void InsertRegRoot(RegisterNum reg, const BaseObject* ref) 75 { 76 if (isValid) { 77 regRoots[reg] = ref; 78 } else { 79 invalidRegRoots[reg] = ref; 80 } 81 } 82 83 protected: 84 template<class MapType> DumpMapGCInfo(const MapType & rootMap,const CString & title,const CString & stringFormat)85 static void DumpMapGCInfo(const MapType& rootMap, const CString& title, const CString& stringFormat) 86 { 87 constexpr size_t numPerRow = 5; 88 constexpr size_t defaultCount = 0; 89 DLOG(ENUM, " %s: {", title.Str()); 90 size_t size = rootMap.size(); 91 size_t remain = size % numPerRow; 92 size_t count = defaultCount; 93 CString detail = ""; 94 for (auto x : rootMap) { 95 if (count == defaultCount) { 96 detail.Append(" "); 97 } 98 detail.Append(CString::FormatString(stringFormat.Str(), x.first, x.second)); 99 if (++count == numPerRow) { 100 count = defaultCount; 101 detail.Append("\n"); 102 } 103 } 104 if (remain != defaultCount) { 105 detail.Append("\n"); 106 } 107 detail.Append(" }\n"); 108 DLOG(ENUM, "%s", detail.Str()); 109 } 110 111 private: 112 CString methodName; 113 uintptr_t startPC; 114 uintptr_t pc; 115 uintptr_t ra; 116 CString timeStamp; 117 std::map<RegisterNum, const BaseObject*> regRoots; 118 std::map<RegisterNum, const BaseObject*> invalidRegRoots; 119 std::map<SlotBias, const BaseObject*> slotRoots; 120 std::map<SlotBias, const BaseObject*> invalidSlotRoots; 121 }; 122 123 class GCInfoNodeForFix : public GCInfoNode { 124 public: BuildNodeForFix(uintptr_t startIP,uintptr_t ip,FrameAddress * fa)125 static GCInfoNodeForFix BuildNodeForFix(uintptr_t startIP, uintptr_t ip, FrameAddress* fa) 126 { 127 CString time = TimeUtil::GetTimestamp(); 128 129 #ifdef __APPLE__ 130 FuncDescRef funcDesc = MFuncDesc::GetFuncDesc(fa); 131 #else 132 FuncDescRef funcDesc = MFuncDesc::GetFuncDesc(startIP); 133 #endif 134 return GCInfoNodeForFix(funcDesc->GetFuncName(), time, startIP, ip, 135 reinterpret_cast<uintptr_t>(fa->returnAddress)); 136 } 137 GCInfoNodeForFix() = default; GCInfoNodeForFix(const CString & name,const CString & time,uintptr_t startIp,uintptr_t ip,uintptr_t retPC)138 GCInfoNodeForFix(const CString& name, const CString& time, uintptr_t startIp, uintptr_t ip, uintptr_t retPC) 139 : GCInfoNode(name, time, startIp, ip, retPC) {} 140 virtual ~GCInfoNodeForFix() = default; InsertDerivedPtrRef(BasePtrType base,DerivedPtrType derived)141 void InsertDerivedPtrRef(BasePtrType base, DerivedPtrType derived) { derivedPtrRef[base] = derived; } DumpFrameGCInfo()142 void DumpFrameGCInfo() const override 143 { 144 GCInfoNode::DumpFrameGCInfo(); 145 DumpMapGCInfo(derivedPtrRef, "derived ptr references", "base object: %#zx, derived ptr: %#zx"); 146 } 147 148 private: 149 std::map<BasePtrType, DerivedPtrType> derivedPtrRef; 150 }; 151 class CurrentGCInfo { 152 public: ~CurrentGCInfo()153 ~CurrentGCInfo(){}; PushFrameInfoForMarking(const GCInfoNode & frameGCInfo)154 void PushFrameInfoForMarking(const GCInfoNode& frameGCInfo) { gcInfosForMarking.push_back(frameGCInfo); } PushFrameInfoForMarking(const GCInfoNode && frameGCInfo)155 void PushFrameInfoForMarking(const GCInfoNode&& frameGCInfo) { gcInfosForMarking.push_back(frameGCInfo); } 156 PushFrameInfoForFix(const GCInfoNodeForFix & frameGCInfo)157 void PushFrameInfoForFix(const GCInfoNodeForFix& frameGCInfo) { gcInfosForFix.push_back(frameGCInfo); } PushFrameInfoForFix(const GCInfoNodeForFix && frameGCInfo)158 void PushFrameInfoForFix(const GCInfoNodeForFix&& frameGCInfo) { gcInfosForFix.push_back(frameGCInfo); } Clear()159 void Clear() 160 { 161 gcInfosForMarking.clear(); 162 std::vector<GCInfoNode>().swap(gcInfosForMarking); 163 gcInfosForFix.clear(); 164 std::vector<GCInfoNodeForFix>().swap(gcInfosForFix); 165 } DumpFrameInfo()166 void DumpFrameInfo() const 167 { 168 DLOG(ENUM, " fix roots info:"); 169 std::for_each(gcInfosForFix.begin(), gcInfosForFix.end(), 170 [](const GCInfoNodeForFix& info) { info.DumpFrameGCInfo(); }); 171 DLOG(ENUM, " marking roots info:"); 172 std::for_each(gcInfosForMarking.begin(), gcInfosForMarking.end(), 173 [](const GCInfoNode& info) { info.DumpFrameGCInfo(); }); 174 } 175 176 private: 177 std::vector<GCInfoNode> gcInfosForMarking; 178 std::vector<GCInfoNodeForFix> gcInfosForFix; 179 }; 180 class GCInfos { 181 public: 182 GCInfos() = default; 183 ~GCInfos() = default; CreateCurrentGCInfo()184 void CreateCurrentGCInfo() 185 { 186 constexpr size_t numLimit = 10; 187 if (gcInfos.size() >= numLimit) { 188 auto& front = gcInfos.front(); 189 front.Clear(); 190 gcInfos.pop_front(); 191 } 192 gcInfos.push_back(CurrentGCInfo()); 193 } 194 PushFrameInfoForMarking(const GCInfoNode & frameGCInfo)195 void PushFrameInfoForMarking(const GCInfoNode& frameGCInfo) 196 { 197 GetCurrentGCInfo().PushFrameInfoForMarking(frameGCInfo); 198 } 199 PushFrameInfoForMarking(const GCInfoNode && frameGCInfo)200 void PushFrameInfoForMarking(const GCInfoNode&& frameGCInfo) 201 { 202 GetCurrentGCInfo().PushFrameInfoForMarking(frameGCInfo); 203 } 204 PushFrameInfoForFix(const GCInfoNodeForFix & infoNodeFoxFix)205 void PushFrameInfoForFix(const GCInfoNodeForFix& infoNodeFoxFix) 206 { 207 GetCurrentGCInfo().PushFrameInfoForFix(infoNodeFoxFix); 208 } PushFrameInfoForFix(const GCInfoNodeForFix && infoNodeForFix)209 void PushFrameInfoForFix(const GCInfoNodeForFix&& infoNodeForFix) 210 { 211 GetCurrentGCInfo().PushFrameInfoForFix(infoNodeForFix); 212 } 213 DumpGCInfos()214 void DumpGCInfos() const 215 { 216 size_t size = gcInfos.size(); 217 DLOG(ENUM, " current thread happened %d times GC", size); 218 size_t i = 1; 219 std::for_each(gcInfos.rbegin(), gcInfos.rend(), [&i](const CurrentGCInfo& cur) { 220 DLOG(ENUM, " the %d scan stack: ", i++); 221 cur.DumpFrameInfo(); 222 }); 223 } 224 225 private: GetCurrentGCInfo()226 CurrentGCInfo& GetCurrentGCInfo() 227 { 228 if (UNLIKELY_CC(gcInfos.empty())) { 229 CreateCurrentGCInfo(); 230 } 231 return gcInfos.back(); 232 } 233 std::list<CurrentGCInfo> gcInfos; 234 }; 235 #endif 236 } // namespace common 237 238 #endif // COMMON_COMPONENTS_HEAP_COLLECTOR_GC_DEBUGGER_H 239