1 /*
2 * Copyright (c) 2024 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
16 #include "ecmascript/dfx/hprof/rawheap_translate/rawheap_translate.h"
17
18 namespace rawheap_translate {
Translate(const std::string & rawheapPath)19 bool RawHeapTranslate::Translate(const std::string &rawheapPath)
20 {
21 uint32_t fileSize = 0;
22 std::ifstream rawheapFile;
23 if (!FileCheckAndOpenBinary(rawheapPath, rawheapFile, fileSize)) {
24 return false;
25 }
26
27 std::vector<uint32_t> sections;
28 if (!ParseMetaData(rawheapFile, fileSize) ||
29 !ReadSectionInfo(rawheapFile, fileSize, sections) ||
30 !ReadVersion(rawheapFile, 0, sections[0]) ||
31 !ReadObjTableBySection(rawheapFile, sections) ||
32 !ReadStringTable(rawheapFile, sections[2], sections[3]) || // 2: str offset, 3: str size
33 !ReadRootTable(rawheapFile, sections[0], sections[1])) {
34 return false;
35 }
36
37 FillNodesAndBuildEdges();
38 return true;
39 }
40
ParseMetaData(std::ifstream & file,uint32_t & offset)41 bool RawHeapTranslate::ParseMetaData(std::ifstream &file, uint32_t &offset)
42 {
43 cJSON *object = nullptr;
44 if (!ReadMetaDataJson(file, offset, &object)) {
45 return false;
46 }
47
48 bool result = meta_->Parse(object);
49 DelMetaDataJson(object);
50 return result;
51 }
52
ReadMetaDataJson(std::ifstream & file,uint32_t & offset,cJSON ** json)53 bool RawHeapTranslate::ReadMetaDataJson(std::ifstream &file, uint32_t &offset, cJSON **json)
54 {
55 auto result = CheckAndGetHead(file, offset - sizeof(uint64_t), 0);
56 if (!result.has_value()) {
57 LOG_ERROR("RawHeapTranslate::ReadMetaDataJson: metadata header error!");
58 return false;
59 }
60
61 auto [metaOffset, metaSize] = result.value();
62 std::vector<char> metaJson(metaSize);
63 if (!ReadFileAtOffset(file, metaOffset, metaSize, metaJson.data())) {
64 return false;
65 }
66
67 cJSON *object = cJSON_ParseWithOpts(metaJson.data(), nullptr, true);
68 if (!object) {
69 LOG_ERROR("RawHeapTranslate::ReadMetaDataJson: json format error!");
70 return false;
71 }
72
73 *json = object;
74 offset = metaOffset;
75 return true;
76 }
77
DelMetaDataJson(cJSON * json)78 void RawHeapTranslate::DelMetaDataJson(cJSON *json)
79 {
80 cJSON_Delete(json);
81 }
82
ReadSectionInfo(std::ifstream & file,uint32_t endOffset,std::vector<uint32_t> & sections)83 bool RawHeapTranslate::ReadSectionInfo(std::ifstream &file, uint32_t endOffset, std::vector<uint32_t> §ions)
84 {
85 auto result = CheckAndGetHead(file, endOffset - sizeof(uint64_t), sizeof(uint32_t));
86 if (!result.has_value()) {
87 LOG_ERROR("RawHeapTranslate::ReadSectionInfo: sections header error!");
88 return false;
89 }
90
91 auto [secSize, unitSize] = result.value();
92 uint32_t sectionSize = secSize * unitSize;
93 std::vector<char> sectionBytes(sectionSize);
94 if (!ReadFileAtOffset(file, endOffset - sectionSize - sizeof(uint64_t), sectionSize, sectionBytes.data())) {
95 return false;
96 }
97
98 sections.resize(secSize);
99 ByteToU32Array(sectionBytes.data(), sections.data(), secSize);
100 LOG_INFO("RawHeapTranslate::ReadSectionInfo: sectionSize=" + std::to_string(secSize));
101 return true;
102 }
103
ReadVersion(std::ifstream & file,uint32_t offset,uint32_t size)104 bool RawHeapTranslate::ReadVersion(std::ifstream &file, uint32_t offset, uint32_t size)
105 {
106 std::vector<char> version(size);
107 if (!ReadFileAtOffset(file, offset, size, version.data())) {
108 return false;
109 }
110 LOG_INFO("Rawheap version is " + std::string(version.data()));
111 return true;
112 }
113
ReadObjTableBySection(std::ifstream & file,const std::vector<uint32_t> & sections)114 bool RawHeapTranslate::ReadObjTableBySection(std::ifstream &file, const std::vector<uint32_t> §ions)
115 {
116 LOG_INFO("RawHeapTranslate::Translate: start to read objects");
117 // 4: index of obj table section start from 4
118 // 2: step is 2
119 for (size_t secIndex = 4; secIndex < sections.size(); secIndex += 2) {
120 uint32_t offset = sections[secIndex];
121 uint32_t size = sections[secIndex + 1];
122 if (!ReadObjTable(file, offset, size)) {
123 return false;
124 }
125 }
126 LOG_INFO("RawHeapTranslate::Translate: read objects finish!");
127 return true;
128 }
129
ReadObjTable(std::ifstream & file,uint32_t offset,uint32_t size)130 bool RawHeapTranslate::ReadObjTable(std::ifstream &file, uint32_t offset, uint32_t size)
131 {
132 auto result = CheckAndGetHead(file, offset, sizeof(AddrTableItem));
133 if (!result.has_value()) {
134 LOG_ERROR("RawHeapTranslate::ReadObjTable: obj table header error!");
135 return false;
136 }
137
138 auto [objNum, unitSize] = result.value();
139 uint32_t baseOffset = sizeof(uint64_t) + offset;
140 std::vector<AddrTableItem> objTable;
141 if (!ByteToAddrTableItem(file, baseOffset, objNum, objTable)) {
142 return false;
143 }
144
145 uint32_t tableSize = objNum * unitSize;
146 uint32_t curOffset = baseOffset + tableSize;
147 uint32_t objMemSize = size - tableSize - sizeof(uint64_t);
148 char *objMem = new char[objMemSize];
149 if (!ReadFileAtOffset(file, curOffset, objMemSize, objMem)) {
150 delete[] objMem;
151 return false;
152 }
153 memBuf_.emplace_back(objMem);
154
155 for (uint32_t i = 0; i < objNum; i++) {
156 uint32_t nextOffset = i + 1 < objNum ? objTable[i + 1].offset : size - sizeof(uint64_t);
157 auto actSize = nextOffset - objTable[i].offset;
158 if (actSize != objTable[i].objSize && actSize != sizeof(uint64_t)) {
159 LOG_ERROR("RawHeapTranslate::ReadObjTable: expected objSize=" + std::to_string(objTable[i].objSize));
160 continue;
161 }
162 uint32_t objMemOffset = objTable[i].offset - tableSize;
163 if (objMemOffset > objMemSize) {
164 LOG_ERROR("RawHeapTranslate::ReadObjTable: obj out of memory buf!");
165 return false;
166 }
167 CreateNode(objTable[i], objMem + objMemOffset);
168 }
169
170 LOG_INFO("RawHeapTranslate::ReadObjTable: read object, cnt=" + std::to_string(objNum));
171 return true;
172 }
173
ReadStringTable(std::ifstream & file,uint32_t offset,uint32_t size)174 bool RawHeapTranslate::ReadStringTable(std::ifstream &file, uint32_t offset, uint32_t size)
175 {
176 if (size == 0) {
177 LOG_ERROR("RawHeapTranslate::ReadStringTable: string section size is 0");
178 return false;
179 }
180 char *strSection = new char[size];
181 if (!ReadFileAtOffset(file, offset, size, strSection)) {
182 delete[] strSection;
183 return false;
184 }
185
186 uint32_t strNum = ByteToU32(strSection);
187 char *curStrSection = strSection + sizeof(uint32_t) * 2;
188 uint32_t strIndex = 0;
189 while (strIndex++ < strNum) {
190 uint32_t strSize = ByteToU32(curStrSection);
191 uint32_t objCnt = ByteToU32(curStrSection + sizeof(uint32_t));
192 char *objAddr = curStrSection + sizeof(uint32_t) * 2;
193 uint32_t strOffset = sizeof(uint32_t) * 2 + sizeof(uint64_t) * objCnt;
194 char *str = curStrSection + strOffset;
195 StringId strId = strTable_->InsertStrAndGetStringId(std::string(str));
196 SetNodeStringId(objAddr, objCnt, strId);
197 curStrSection += strOffset + strSize + 1;
198 }
199
200 delete[] strSection;
201 LOG_INFO("RawHeapTranslate::ReadStringTable: read string table, cnt=" + std::to_string(strNum));
202 return true;
203 }
204
ReadRootTable(std::ifstream & file,uint32_t offset,uint32_t size)205 bool RawHeapTranslate::ReadRootTable(std::ifstream &file, uint32_t offset, uint32_t size)
206 {
207 auto result = CheckAndGetHead(file, offset, sizeof(uint64_t));
208 if (!result.has_value()) {
209 LOG_ERROR("RawHeapTranslate::ReadObjTable: obj table header error!");
210 return false;
211 }
212
213 auto [rootNum, unitSize] = result.value();
214 std::vector<char> roots(rootNum * unitSize);
215 if (!ReadFileAtOffset(file, offset + sizeof(uint64_t), rootNum * unitSize, roots.data())) {
216 return false;
217 }
218
219 auto syntheticRoot = std::make_shared<Node>(Node(0));
220 syntheticRoot->nodeId = 1; // 1: means root node
221 syntheticRoot->type = 9; // 9: means SYNTHETIC node type
222 syntheticRoot->strId = strTable_->InsertStrAndGetStringId("SyntheticRoot");
223 nodes_.insert(nodes_.begin(), syntheticRoot);
224 StringId edgeStrId = strTable_->InsertStrAndGetStringId("-subroot-");
225 EdgeType type = EdgeType::SHORTCUT;
226
227 char *addr = roots.data();
228 std::vector<std::shared_ptr<Edge>> rootEdges;
229 for (uint32_t i = 0; i < rootNum; i++) {
230 uint64_t rootAddr = ByteToU64(addr);
231 addr += sizeof(uint64_t);
232 auto rootNode = nodesMap_.find(rootAddr);
233 if (rootNode == nodesMap_.end()) {
234 continue;
235 }
236 auto edge = std::make_shared<Edge>(Edge(type, syntheticRoot, rootNode->second, edgeStrId));
237 rootEdges.emplace_back(edge);
238 syntheticRoot->edgeCount++;
239 }
240
241 edges_.insert(edges_.begin(), rootEdges.begin(), rootEdges.end());
242 LOG_INFO("RawHeapTranslate::ReadRootTable: find root obj " + std::to_string(rootNum));
243 return true;
244 }
245
ReadFileAtOffset(std::ifstream & file,uint32_t offset,uint32_t size,char * buf)246 bool RawHeapTranslate::ReadFileAtOffset(std::ifstream &file, uint32_t offset, uint32_t size, char *buf)
247 {
248 if (buf == nullptr) {
249 LOG_ERROR("RawHeapTranslate::ReadFileAtOffset: file buf is nullptr!");
250 return false;
251 }
252 if (!file.is_open()) {
253 LOG_ERROR("RawHeapTranslate::ReadFileAtOffset: file not open!");
254 return false;
255 }
256 file.clear();
257 if (!file.seekg(offset)) {
258 LOG_ERROR("RawHeapTranslate::ReadFileAtOffset: file set offset failed, offset=" + std::to_string(offset));
259 return false;
260 }
261 if (file.read(buf, size).fail()) {
262 LOG_ERROR("RawHeapTranslate::ReadFileAtOffset: file read failed, offset=" + std::to_string(offset));
263 return false;
264 }
265 return true;
266 }
267
CreateNode(AddrTableItem & item,char * data)268 void RawHeapTranslate::CreateNode(AddrTableItem &item, char *data)
269 {
270 static uint32_t nodeIndex = 1;
271 auto node = std::make_shared<Node>(Node(nodeIndex++));
272 node->nodeId = item.id;
273 node->size = item.objSize;
274 node->data = data;
275 nodes_.emplace_back(node);
276 nodesMap_.emplace(item.addr, node);
277 }
278
FillNodesAndBuildEdges()279 void RawHeapTranslate::FillNodesAndBuildEdges()
280 {
281 LOG_INFO("RawHeapTranslate::FillNodesAndBuildEdges: start to build edges!");
282 int missNodes = 0;
283 StringId hclassStrId = strTable_->InsertStrAndGetStringId("hclass");
284 for (size_t i = 1; i < nodes_.size(); i++) {
285 auto node = nodes_[i];
286 auto result = FindNodeFromAddr(ByteToU64(node->data), nullptr);
287 if (!result.has_value()) {
288 missNodes++;
289 continue;
290 }
291 auto hclass = result.value();
292 FillNodes(node, hclass->data);
293 if (hclass->nodeId == node->nodeId) {
294 continue;
295 }
296 CreateEdge(node, hclass, EdgeType::DEFAULT, hclassStrId);
297 if (meta_->IsString(hclass->data)) {
298 continue;
299 }
300 BuildEdges(node, hclass->data);
301 }
302 LOG_INFO("RawHeapTranslate::FillNodesAndBuildEdges: build edges finish!");
303 if (missNodes > 0) {
304 LOG_ERROR("RawHeapTranslate::FillNodesAndBuildEdges: " + std::to_string(missNodes) + " nodes missed hclass!");
305 }
306 }
307
FillNodes(const std::shared_ptr<Node> & node,char * hclass)308 void RawHeapTranslate::FillNodes(const std::shared_ptr<Node> &node, char *hclass)
309 {
310 node->nativeSize = meta_->GetNativateSize(node->data, hclass);
311 node->type = meta_->GetNodeTypeFromHClass(hclass);
312 if (node->strId < 3 && !meta_->IsString(hclass)) { // 3: custom strId start from 3
313 std::string name = meta_->GetTypeNameFromHClass(hclass);
314 std::transform(name.begin(), name.end(), name.begin(), ::tolower);
315 node->strId = strTable_->InsertStrAndGetStringId(name);
316 }
317 }
318
BuildEdges(const std::shared_ptr<Node> & from,char * hclass)319 void RawHeapTranslate::BuildEdges(const std::shared_ptr<Node> &from, char *hclass)
320 {
321 if (meta_->IsGlobalEnv(hclass)) {
322 BuildGlobalEnvEdges(from);
323 return;
324 }
325
326 std::string typeName = meta_->GetTypeNameFromHClass(hclass);
327 auto visitor = [&from, this] (std::shared_ptr<MetaData> &metadata, uint32_t offset) {
328 if (!metadata->IsArray()) {
329 BuildFieldsEdges(from, metadata, offset);
330 return;
331 }
332 BuildArrayEdges(from, metadata, offset);
333 };
334 uint32_t baseOffset = 0;
335 meta_->VisitObjectBody(typeName, visitor, baseOffset);
336 BuildJSObjectInlEdges(from, hclass, baseOffset);
337 }
338
BuildFieldsEdges(const std::shared_ptr<Node> & from,const std::shared_ptr<MetaData> & metadata,uint32_t offset)339 void RawHeapTranslate::BuildFieldsEdges(
340 const std::shared_ptr<Node> &from, const std::shared_ptr<MetaData> &metadata, uint32_t offset)
341 {
342 for (const auto &field : metadata->fields) {
343 if (field->size != sizeof(uint64_t) || offset + sizeof(uint64_t) > from->size) {
344 continue;
345 }
346 uint64_t addr = ByteToU64(from->data + offset + field->offset);
347 EdgeType type = EdgeType::DEFAULT;
348 auto result = FindNodeFromAddr(addr, &type);
349 if (!result.has_value()) {
350 continue;
351 }
352 StringId strId = strTable_->InsertStrAndGetStringId(field->name);
353 CreateEdge(from, result.value(), type, strId);
354 }
355 }
356
BuildGlobalEnvEdges(const std::shared_ptr<Node> & from)357 void RawHeapTranslate::BuildGlobalEnvEdges(const std::shared_ptr<Node> &from)
358 {
359 uint32_t index = sizeof(uint64_t);
360 while ((index + sizeof(uint64_t)) < from->size) {
361 uint64_t addr = ByteToU64(from->data + index);
362 index += sizeof(uint64_t);
363 auto result = FindNodeFromAddr(addr, nullptr);
364 if (!result.has_value()) {
365 continue;
366 }
367 CreateEdge(from, result.value(), EdgeType::DEFAULT, result.value()->strId);
368 }
369 }
370
BuildArrayEdges(const std::shared_ptr<Node> & from,const std::shared_ptr<MetaData> & metadata,uint32_t offset)371 void RawHeapTranslate::BuildArrayEdges(
372 const std::shared_ptr<Node> &from, const std::shared_ptr<MetaData> &metadata, uint32_t offset)
373 {
374 auto lengthField = FindFieldInMetaData(metadata, "Length");
375 auto dataField = FindFieldInMetaData(metadata, "Data");
376 if (!lengthField || !dataField) {
377 LOG_ERROR("RawHeapTranslate::BuildArrayEdges: field length or data of array not found!");
378 return;
379 }
380
381 uint32_t len = ByteToU32(from->data + offset + lengthField->offset);
382 uint32_t step = dataField->size;
383 if (step != sizeof(uint64_t) || len <= 0 || len * step + offset > from->size) {
384 return;
385 }
386
387 uint32_t index = offset + dataField->offset;
388 uint32_t itemIndex = 0;
389 uint32_t itemCnt = 0;
390 while (itemCnt++ < len && index < from->size) {
391 uint64_t addr = ByteToU64(from->data + index);
392 index += step;
393 auto result = FindNodeFromAddr(addr, nullptr);
394 if (!result.has_value()) {
395 continue;
396 }
397 CreateEdge(from, result.value(), EdgeType::ELEMENT, itemIndex++);
398 }
399 }
400
BuildDictionaryEdges(const std::shared_ptr<Node> & from,const std::shared_ptr<MetaData> & metadata,uint32_t offset)401 void RawHeapTranslate::BuildDictionaryEdges(
402 const std::shared_ptr<Node> &from, const std::shared_ptr<MetaData> &metadata, uint32_t offset)
403 {
404 auto lengthField = FindFieldInMetaData(metadata, "Length");
405 auto dataField = FindFieldInMetaData(metadata, "Data");
406 auto layout = meta_->GetObjectLayout("Dictionary");
407 if (!lengthField || !dataField || !layout) {
408 LOG_ERROR("RawHeapTranslate::BuildDictionaryEdges: field length or data or layout of dictionary not found!");
409 return;
410 }
411
412 uint32_t len = ByteToU32(from->data + offset + lengthField->offset);
413 uint32_t step = dataField->size;
414 if (step != sizeof(uint64_t) || len <= 0 || len * step + offset > from->size) {
415 return;
416 }
417
418 uint32_t eleIndex = 0;
419 uint32_t index = offset + dataField->offset + layout->headerSize * step;
420 while (index < from->size) {
421 std::vector<uint64_t> item(layout->entrySize);
422 ByteToU64Array(from->data + index, item.data(), layout->entrySize);
423 index += layout->entrySize * step;
424
425 auto value = FindNodeFromAddr(item[layout->valueIndex], nullptr);
426 if (!value.has_value()) {
427 continue;
428 }
429 CreateEdge(from, value.value(), EdgeType::ELEMENT, eleIndex++);
430 }
431 }
432
BuildJSObjectInlEdges(const std::shared_ptr<Node> & from,char * hclass,uint32_t offset)433 void RawHeapTranslate::BuildJSObjectInlEdges(const std::shared_ptr<Node> &from, char *hclass, uint32_t offset)
434 {
435 if (!meta_->IsJSObject(hclass)) {
436 return;
437 }
438
439 StringId strId = strTable_->InsertStrAndGetStringId("InlineProperty");
440 uint32_t propOffset = offset;
441 while (propOffset + sizeof(uint64_t) <= from->size) {
442 uint64_t addr = ByteToU64(from->data + propOffset);
443 propOffset += sizeof(uint64_t);
444 auto result = FindNodeFromAddr(addr, nullptr);
445 if (!result.has_value()) {
446 continue;
447 }
448 CreateEdge(from, result.value(), EdgeType::DEFAULT, strId);
449 }
450 }
451
CreateEdge(const std::shared_ptr<Node> & from,const std::shared_ptr<Node> & to,EdgeType type,uint32_t index)452 void RawHeapTranslate::CreateEdge(
453 const std::shared_ptr<Node> &from, const std::shared_ptr<Node> &to, EdgeType type, uint32_t index)
454 {
455 auto edge = std::make_shared<Edge>(Edge(type, from, to, index));
456 edges_.emplace_back(edge);
457 from->edgeCount++;
458 }
459
SetNodeStringId(char * addr,uint32_t size,StringId strId)460 void RawHeapTranslate::SetNodeStringId(char *addr, uint32_t size, StringId strId)
461 {
462 char *objAddr = addr;
463 for (uint32_t i = 0; i < size; i++) {
464 auto node = nodesMap_.find(ByteToU64(objAddr));
465 if (node != nodesMap_.end()) {
466 node->second->strId = strId;
467 }
468 objAddr += sizeof(uint64_t);
469 }
470 }
471
ByteToAddrTableItem(std::ifstream & file,uint32_t offset,uint32_t objNum,std::vector<AddrTableItem> & table)472 bool RawHeapTranslate::ByteToAddrTableItem(
473 std::ifstream &file, uint32_t offset, uint32_t objNum, std::vector<AddrTableItem> &table)
474 {
475 uint32_t unitSize = sizeof(AddrTableItem);
476 uint32_t tableSize = objNum * unitSize;
477 std::vector<char> tableByte(tableSize);
478 if (!ReadFileAtOffset(file, offset, tableSize, tableByte.data())) {
479 return false;
480 }
481 table.resize(objNum);
482 char *item = tableByte.data();
483 for (uint32_t i = 0; i < objNum; i++) {
484 table[i].addr = ByteToU64(item);
485 item += sizeof(uint64_t);
486
487 table[i].id = ByteToU64(item);
488 item += sizeof(uint64_t);
489
490 table[i].objSize = ByteToU32(item);
491 item += sizeof(uint32_t);
492
493 table[i].offset = ByteToU32(item);
494 item += sizeof(uint32_t);
495 }
496 return true;
497 }
498
FindNodeFromAddr(uint64_t addr,EdgeType * type)499 std::optional<std::shared_ptr<Node>> RawHeapTranslate::FindNodeFromAddr(uint64_t addr, EdgeType *type)
500 {
501 if (!IsHeapObject(addr)) {
502 return std::nullopt;
503 }
504 CheckAndRemoveWeak(addr, type);
505 auto node = nodesMap_.find(addr);
506 if (node == nodesMap_.end()) {
507 return std::nullopt;
508 }
509 return node->second;
510 }
511
CheckAndGetHead(std::ifstream & file,uint32_t offset,uint32_t assertNum)512 std::optional<std::pair<uint32_t, uint32_t>> RawHeapTranslate::CheckAndGetHead(
513 std::ifstream &file, uint32_t offset, uint32_t assertNum)
514 {
515 uint32_t headSize = sizeof(uint64_t);
516 std::vector<char> head(headSize);
517 if (!ReadFileAtOffset(file, offset, headSize, head.data())) {
518 return std::nullopt;
519 }
520
521 uint32_t firstNum = ByteToU32(head.data());
522 uint32_t secondNum = ByteToU32(head.data() + sizeof(uint32_t));
523 if (assertNum != 0 && secondNum != assertNum) {
524 return std::nullopt;
525 }
526
527 return std::pair<uint32_t, uint32_t>(firstNum, secondNum);
528 }
529
FindFieldInMetaData(const std::shared_ptr<MetaData> & metadata,const std::string & name)530 std::shared_ptr<Field> RawHeapTranslate::FindFieldInMetaData(
531 const std::shared_ptr<MetaData> &metadata, const std::string &name)
532 {
533 for (const auto &field : metadata->fields) {
534 if (field->name == name) {
535 return field;
536 }
537 }
538 return nullptr;
539 }
540
CheckAndRemoveWeak(uint64_t & addr,EdgeType * type)541 void RawHeapTranslate::CheckAndRemoveWeak(uint64_t &addr, EdgeType *type)
542 {
543 if ((addr & TAG_WEAK_MASK) == TAG_WEAK) {
544 addr &= (~TAG_WEAK);
545 if (type != nullptr) {
546 *type = EdgeType::WEAK;
547 }
548 }
549 }
550
IsHeapObject(uint64_t addr)551 bool RawHeapTranslate::IsHeapObject(uint64_t addr)
552 {
553 return ((addr & TAG_HEAPOBJECT_MASK) == 0U);
554 }
555 } // namespace rawheap_translate