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/metadata_parse.h"
17
18 namespace rawheap_translate {
Parse(const cJSON * object)19 bool Meta::Parse(const cJSON *object)
20 {
21 return ParseVersion(object) &&
22 ParseTypeEnums(object) &&
23 ParseTypeList(object) &&
24 ParseTypeLayout(object) &&
25 SetObjTypeBitFieldOffset() &&
26 SetNativatePointerBindingSizeOffset();
27 }
28
GetNativateSize(char * obj,char * hclass)29 uint32_t Meta::GetNativateSize(char *obj, char *hclass)
30 {
31 JSType typeId = GetObjTypeFromHClass(hclass);
32 if (typeId != GetObjTypeFromTypeName("JS_NATIVE_POINTER")) {
33 return 0;
34 }
35 return ByteToU32(obj + nativatePointerBindingSizeOffset_);
36 }
37
GetTypeNameFromHClass(char * hclass)38 std::string Meta::GetTypeNameFromHClass(char *hclass)
39 {
40 JSType typeId = GetObjTypeFromHClass(hclass);
41 if (typeId < enumsVec_.size()) {
42 return enumsVec_[typeId];
43 }
44 LOG_ERROR("Meta::GetTypeNameFromTypeId: typeId out of range, typeId=" + std::to_string(typeId));
45 return "UNKNOWN_TYPE";
46 }
47
GetObjectLayout(const std::string & name)48 std::shared_ptr<Layout> Meta::GetObjectLayout(const std::string &name)
49 {
50 auto layout = layout_.find(name);
51 if (layout != layout_.end()) {
52 return layout->second;
53 }
54 return nullptr;
55 }
56
IsString(char * hclass)57 bool Meta::IsString(char *hclass)
58 {
59 JSType typeId = GetObjTypeFromHClass(hclass);
60 JSType stringFirst = GetObjTypeFromTypeName(GetTypeDesc("string_first"));
61 JSType stringLast = GetObjTypeFromTypeName(GetTypeDesc("string_last"));
62 if (stringFirst <= typeId && typeId <= stringLast) {
63 return true;
64 }
65 return false;
66 }
67
IsDictionaryMode(char * hclass)68 bool Meta::IsDictionaryMode(char *hclass)
69 {
70 return GetObjTypeFromHClass(hclass) == GetObjTypeFromTypeName("TAGGED_DICTIONARY");
71 }
72
IsJSObject(char * hclass)73 bool Meta::IsJSObject(char *hclass)
74 {
75 JSType typeId = GetObjTypeFromHClass(hclass);
76 JSType stringFirst = GetObjTypeFromTypeName(GetTypeDesc("js_object_first"));
77 JSType stringLast = GetObjTypeFromTypeName(GetTypeDesc("js_object_last"));
78 if (stringFirst <= typeId && typeId <= stringLast) {
79 return true;
80 }
81 return false;
82 }
83
IsGlobalEnv(char * hclass)84 bool Meta::IsGlobalEnv(char *hclass)
85 {
86 return GetObjTypeFromHClass(hclass) == GetObjTypeFromTypeName("GLOBAL_ENV");
87 }
88
GetNodeTypeFromHClass(char * hclass)89 NodeType Meta::GetNodeTypeFromHClass(char *hclass)
90 {
91 std::string name = GetTypeNameFromHClass(hclass);
92 auto type = enumsMapNodeType_.find(name);
93 if (type != enumsMapNodeType_.end()) {
94 return type->second;
95 }
96 return DEFAULT_NODETYPE; // 8: means DEFAULT node type
97 }
98
VisitObjectBody(const std::string & name,const ObjRangeVisitor & visitor,uint32_t & baseOffset)99 void Meta::VisitObjectBody(const std::string &name, const ObjRangeVisitor &visitor, uint32_t &baseOffset)
100 {
101 auto metadata = GetMetaData(name);
102 if (!metadata) {
103 return;
104 }
105
106 for (const auto &parent : metadata->parents) {
107 VisitObjectBody(parent, visitor, baseOffset);
108 }
109
110 visitor(metadata, baseOffset);
111 baseOffset += metadata->endOffset;
112 }
113
SetObjTypeBitFieldOffset()114 bool Meta::SetObjTypeBitFieldOffset()
115 {
116 auto visitor = [this] (std::shared_ptr<MetaData> &metadata, uint32_t offset) {
117 for (const auto &field : metadata->fields) {
118 if (field->name == "BitField") {
119 objTypeBitFieldOffset_ = offset + field->offset;
120 objTypeBitFieldSize_ = field->size;
121 }
122 }
123 };
124 uint32_t baseOffset = 0;
125 VisitObjectBody("HCLASS", visitor, baseOffset);
126 LOG_INFO("Meta::SetObjTypeBitFieldOffset: offset=" + std::to_string(objTypeBitFieldOffset_));
127 return true;
128 }
129
SetNativatePointerBindingSizeOffset()130 bool Meta::SetNativatePointerBindingSizeOffset()
131 {
132 auto visitor = [this] (std::shared_ptr<MetaData> &metadata, uint32_t offset) {
133 for (const auto &field : metadata->fields) {
134 if (field->name == "BindingSize") {
135 nativatePointerBindingSizeOffset_ = offset + field->offset;
136 nativatePointerBindingSize_ = field->size;
137 }
138 }
139 };
140 uint32_t baseOffset = 0;
141 VisitObjectBody("JS_NATIVE_POINTER", visitor, baseOffset);
142 LOG_INFO("Meta::SetNativatePointerBindingSizeOffset: offset=" + std::to_string(nativatePointerBindingSizeOffset_));
143 return true;
144 }
145
GetObjTypeFromHClass(char * hclass)146 JSType Meta::GetObjTypeFromHClass(char *hclass)
147 {
148 return ByteToU32(hclass + objTypeBitFieldOffset_) & 0xFF; // lower 8-bits of 32-bit value means JSType
149 }
150
GetObjTypeFromTypeName(const std::string & name)151 JSType Meta::GetObjTypeFromTypeName(const std::string &name)
152 {
153 auto typeEnum = enumsMapJSType_.find(name);
154 if (typeEnum == enumsMapJSType_.end()) {
155 return 0;
156 }
157 return typeEnum->second;
158 }
159
GetTypeDesc(const std::string & name)160 std::string Meta::GetTypeDesc(const std::string &name)
161 {
162 auto result = typeDesc_.find(name);
163 if (result != typeDesc_.end()) {
164 return result->second;
165 }
166 return "UNKNOWN_TYPE";
167 }
168
ParseTypeEnums(const cJSON * json)169 bool Meta::ParseTypeEnums(const cJSON *json)
170 {
171 cJSON *typeEnums = cJSON_GetObjectItem(json, "type_enum");
172 auto visit = [&typeEnums, this] (const cJSON *item, int index) {
173 std::string name = item->string;
174 uint32_t edgeType = 0;
175 if (GetUInt32(typeEnums, name.c_str(), edgeType)) {
176 enumsVec_.push_back(name);
177 enumsMapJSType_.emplace(name, static_cast<JSType>(index));
178 enumsMapNodeType_.emplace(name, static_cast<uint8_t>(edgeType));
179 }
180 };
181 IterateJSONArray(typeEnums, visit);
182 LOG_INFO("Meta::ParseTypeEnums: parse type enums, size=" + std::to_string(enumsVec_.size()));
183 return true;
184 }
185
ParseTypeList(const cJSON * json)186 bool Meta::ParseTypeList(const cJSON *json)
187 {
188 cJSON *metadatas {nullptr};
189 std::vector<std::string> *curParents {nullptr};
190 std::vector<std::shared_ptr<Field>> *curField {nullptr};
191 if (!GetArray(json, "type_list", &metadatas)) {
192 LOG_ERROR("Meta::ParseTypeList: type list parse failed!");
193 return false;
194 }
195
196 auto parentVisitor = [&curParents] (const cJSON *item, [[maybe_unused]] int index) {
197 std::string value {};
198 if (GetString(item, value)) {
199 curParents->push_back(value);
200 }
201 };
202
203 auto offsetVisitor = [&curField] (const cJSON *item, [[maybe_unused]] int index) {
204 auto field = std::make_shared<Field>();
205 if (GetUInt32(item, "offset", field->offset) &&
206 GetString(item, "name", field->name) &&
207 GetUInt32(item, "size", field->size)) {
208 curField->push_back(field);
209 }
210 };
211
212 auto metaVisitor =
213 [&curParents, &curField, &parentVisitor, &offsetVisitor, this] (const cJSON *item, [[maybe_unused]] int index) {
214 cJSON *fields {nullptr};
215 cJSON *parents {nullptr};
216 if (!GetArray(item, "offsets", &fields) || !GetArray(item, "parents", &parents)) {
217 return;
218 }
219
220 auto metadata = std::make_shared<MetaData>();
221 curParents = &metadata->parents;
222 curField = &metadata->fields;
223 IterateJSONArray(fields, offsetVisitor);
224 IterateJSONArray(parents, parentVisitor);
225
226 if (GetString(item, "name", metadata->name) && GetUInt32(item, "end_offset", metadata->endOffset)) {
227 metadata_.emplace(metadata->name, metadata);
228 }
229
230 GetString(item, "visit_type", metadata->visitType);
231 };
232
233 IterateJSONArray(metadatas, metaVisitor);
234 LOG_INFO("Meta::ParseTypeList: parse type list, obj size = " + std::to_string(metadata_.size()));
235 return true;
236 }
237
ParseTypeLayout(const cJSON * json)238 bool Meta::ParseTypeLayout(const cJSON *json)
239 {
240 cJSON *object = cJSON_GetObjectItem(json, "type_layout");
241
242 cJSON *dictLayout = cJSON_GetObjectItem(object, "Dictionary_layout");
243 auto layout = std::make_shared<Layout>();
244 GetString(dictLayout, "name", layout->name);
245 GetUInt32(dictLayout, "key_index", layout->keyIndex);
246 GetUInt32(dictLayout, "value_index", layout->valueIndex);
247 GetUInt32(dictLayout, "detail_index", layout->detailIndex);
248 GetUInt32(dictLayout, "entry_size", layout->entrySize);
249 GetUInt32(dictLayout, "header_size", layout->headerSize);
250 layout_.emplace(layout->name, layout);
251 LOG_INFO("Meta::ParseTypeLayout: parse type layout, size=" + std::to_string(layout_.size()));
252
253 cJSON *typeRange = cJSON_GetObjectItem(object, "Type_range");
254 auto visit = [&typeRange, this] (const cJSON *item, [[maybe_unused]] int index) {
255 std::string key = item->string;
256 std::string value {};
257 if (GetString(typeRange, key.c_str(), value)) {
258 typeDesc_.emplace(key, value);
259 }
260 };
261 IterateJSONArray(typeRange, visit);
262 LOG_INFO("Meta::ParseTypeLayout: parse type desc, size=" + std::to_string(typeDesc_.size()));
263 return true;
264 }
265
ParseVersion(const cJSON * json)266 bool Meta::ParseVersion(const cJSON *json)
267 {
268 std::string version {};
269 if (!GetString(json, "version", version)) {
270 LOG_ERROR("Meta::ParseVersion: version not found!");
271 return false;
272 }
273
274 if (!CheckVersion(version)) {
275 return false;
276 }
277
278 LOG_INFO("Meta::ParseVersion: current metadata version is " + version);
279 return true;
280 }
GetMetaData(const std::string & name)281 std::shared_ptr<MetaData> Meta::GetMetaData(const std::string &name)
282 {
283 auto metadata = metadata_.find(name);
284 if (metadata != metadata_.end()) {
285 return metadata->second;
286 }
287 return nullptr;
288 }
289
IterateJSONArray(const cJSON * array,const std::function<void (const cJSON *,int)> & visitor)290 void Meta::IterateJSONArray(const cJSON *array, const std::function<void(const cJSON *, int)> &visitor)
291 {
292 int size = cJSON_GetArraySize(array);
293 for (int i = 0; i < size; i++) {
294 cJSON *item = cJSON_GetArrayItem(array, i);
295 visitor(item, i);
296 }
297 }
298
GetArray(const cJSON * json,const char * key,cJSON ** value)299 bool Meta::GetArray(const cJSON *json, const char *key, cJSON **value)
300 {
301 cJSON *array = cJSON_GetObjectItem(json, key);
302 if (array == nullptr || cJSON_IsArray(array) == 0) {
303 return false;
304 }
305 *value = array;
306 return true;
307 }
308
GetString(const cJSON * json,const char * key,std::string & value)309 bool Meta::GetString(const cJSON *json, const char *key, std::string &value)
310 {
311 cJSON *item = cJSON_GetObjectItem(json, key);
312 if (item == nullptr) {
313 return false;
314 }
315 return GetString(item, value);
316 }
317
GetString(const cJSON * json,std::string & value)318 bool Meta::GetString(const cJSON *json, std::string &value)
319 {
320 if (cJSON_IsString(json) == 0) {
321 return false;
322 }
323 value = json->valuestring;
324 return true;
325 }
326
GetUInt32(const cJSON * json,const char * key,uint32_t & value)327 bool Meta::GetUInt32(const cJSON *json, const char *key, uint32_t &value)
328 {
329 cJSON *item = cJSON_GetObjectItem(json, key);
330 if (item == nullptr) {
331 return false;
332 }
333 return GetUInt32(item, value);
334 }
335
GetUInt32(const cJSON * json,uint32_t & value)336 bool Meta::GetUInt32(const cJSON *json, uint32_t &value)
337 {
338 if (cJSON_IsNumber(json) == 0) {
339 return false;
340 }
341 if (json->valuedouble < 0 || json->valuedouble > static_cast<double>(UINT32_MAX)) {
342 return false;
343 }
344 value = static_cast<uint32_t>(json->valuedouble);
345 return true;
346 }
347 } // namespace rawheap_translate