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