1/*
2 * Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved.
3 * Description: improve heap dump.
4 * Create: 2020/11/20
5 */
6
7#include "heapdump.h"
8
9#include "ecma-builtins.h"
10#include "ecma-conversion.h"
11#include "ecma-gc.h"
12#include "ecma-globals.h"
13#include "ecma-helpers.h"
14#include "ecma-property-hashmap.h"
15#include "ecma-array-object.h"
16
17#include <stdio.h>
18
19bool gHeapdumpTracing = false;
20FILE* gLogHeapdumpFile = NULL;
21bool GetHeapdumpTracing(void)
22{
23  return gHeapdumpTracing;
24}
25
26void SetHeapdumpTraring(bool flag)
27{
28  gHeapdumpTracing = flag;
29}
30
31FILE* GetHeapdumpFile(void)
32{
33  return gLogHeapdumpFile;
34}
35
36void LogHeapdumpInit(const char* filepath)
37{
38  gLogHeapdumpFile = fopen(filepath, "w+");
39}
40
41void LogHeapdumpClose(void)
42{
43  fclose(gLogHeapdumpFile);
44}
45
46static void StartList(void)
47{
48  LogHeapdump("[\n");
49}
50
51static void EndList(void)
52{
53  LogHeapdump("]\n");
54}
55
56static void Start(void)
57{
58  LogHeapdump("{\n");
59}
60
61static void End(void)
62{
63  LogHeapdump("}\n");
64}
65
66static void Next(void)
67{
68  LogHeapdump(",\n");
69}
70
71static void LogStr(const char* str)
72{
73  LogHeapdump("\"%s\"\n", str);
74}
75
76static void LogAddr(void* addr)
77{
78  LogHeapdump("\"%p\"\n", addr);
79}
80
81static void LogUint(unsigned int val)
82{
83  LogHeapdump("%u\n", val);
84}
85
86static void LogStrObj(const ecma_string_t* obj)
87{
88  ECMA_STRING_TO_UTF8_STRING(obj, str, str_size);
89  LogHeapdump("\"");
90  for (int ii = 0; ii < (int)str_size; ++ii) {
91    LogHeapdump("%c", str[ii]);
92  }
93  LogHeapdump("\"\n");
94}
95
96static void Key(const char* key)
97{
98  LogHeapdump("\"%s\"", key);
99  LogHeapdump(":\n");
100}
101
102static void KeyUint(unsigned int key)
103{
104  LogHeapdump("\"%u\"", key);
105  LogHeapdump(":\n");
106}
107
108static void Type(const char* type)
109{
110  Key("type");
111  LogHeapdump("\"%s\"", type);
112  Next();
113}
114
115static void Addr(void* addr)
116{
117  Key("addr");
118  LogHeapdump("\"%p\"", addr);
119  Next();
120}
121
122void DumpInfoLexEnv(const ecma_object_t* object)
123{
124  Key("outer");
125  jmem_cpointer_t outer_lex_env_cp = object->u2.outer_reference_cp;
126  if (outer_lex_env_cp != JMEM_CP_NULL) {
127    LogAddr(ECMA_GET_NON_NULL_POINTER(ecma_object_t, outer_lex_env_cp));
128  } else {
129    LogAddr(NULL);
130  }
131  Next();
132
133  Key("subtype");
134  if (ecma_get_lex_env_type(object) != ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE) {
135    LogStr("binding");
136    Next();
137
138    Key("binding");
139    ecma_object_t *binding_object_p = ecma_get_lex_env_binding_object(object);
140    LogAddr(binding_object_p);
141  } else {
142    LogStr("declarative");
143  }
144}
145
146void DumpInfoFunction(const ecma_object_t* object)
147{
148  Key("is_builtin");
149  ecma_extended_object_t* ext_obj = (ecma_extended_object_t*)object;
150  if (ecma_get_object_is_builtin (object)) {
151    LogStr("true");
152    Next();
153
154    Key("is_routine");
155    if (ecma_builtin_function_is_routine ((ecma_object_t*)object)) {
156      LogStr("true");
157    } else {
158      LogStr("true");
159    }
160    Next();
161
162    Key("id");
163    LogUint(ext_obj->u.built_in.id);
164    Next();
165    Key("routine_id");
166    LogUint(ext_obj->u.built_in.routine_id);
167  } else {
168    LogStr("false");
169    Next();
170
171    Key("scope");
172    LogAddr(ECMA_GET_INTERNAL_VALUE_POINTER(ecma_object_t, ext_obj->u.function.scope_cp));
173  }
174}
175
176void DumpPropertyPair(ecma_property_pair_t* pair)
177{
178  ecma_property_header_t* header = (ecma_property_header_t*)pair;
179  for (int i = 0; i < ECMA_PROPERTY_PAIR_ITEM_COUNT; i++) {
180    Start();
181
182    switch (ECMA_PROPERTY_GET_TYPE(header->types[i])) {
183      case ECMA_PROPERTY_TYPE_NAMEDDATA: {
184        Type("data");
185
186        Key("key");
187        ecma_string_t* key_str = ecma_string_from_property_name(header->types[i],
188                                                                pair->names_cp[i]);
189        LogStrObj(key_str);
190        ecma_deref_ecma_string(key_str);
191        Next();
192
193        Key("value");
194        ecma_value_t value = pair->values[i].value;
195        if (ecma_is_value_object(value)) {
196          ecma_object_t* value_obj = ecma_get_object_from_value(value);
197          LogAddr(value_obj);
198        } else {
199          ecma_string_t* value_str = ecma_op_to_string(value);
200          LogStrObj(value_str);
201        }
202        break;
203      }
204      case ECMA_PROPERTY_TYPE_NAMEDACCESSOR: {
205        Type("accessor");
206
207        ecma_property_value_t* accessor_objs_p = pair->values + i;
208        ecma_getter_setter_pointers_t* get_set_pair_p =
209            ecma_get_named_accessor_property(accessor_objs_p);
210
211        Key("getter");
212        if (get_set_pair_p->getter_cp != JMEM_CP_NULL) {
213          LogAddr(ECMA_GET_NON_NULL_POINTER(ecma_object_t, get_set_pair_p->getter_cp));
214        } else {
215          LogAddr(NULL);
216        }
217        Next();
218
219        Key("setter");
220        if (get_set_pair_p->setter_cp != JMEM_CP_NULL) {
221          LogAddr(ECMA_GET_NON_NULL_POINTER(ecma_object_t, get_set_pair_p->setter_cp));
222        } else {
223          LogAddr(NULL);
224        }
225        break;
226      }
227      case ECMA_PROPERTY_TYPE_INTERNAL: {
228        Type("internal");
229        Key("TODO");
230        LogStr("Not implemented yet");
231        break;
232      }
233      default: {
234        break;
235      }
236    }
237    End();
238    if (i + 1 < ECMA_PROPERTY_PAIR_ITEM_COUNT) {
239      Next();
240    }
241  }
242}
243
244void DumpInfoObject(ecma_object_t* object, heapdump_object_flags_t flags)
245{
246  Start();
247  Addr(object);
248  if (flags & HEAPDUMP_OBJECT_ROOT) {
249    Key("Root");
250    LogStr("true");
251    Next();
252  }
253
254  if (flags & HEAPDUMP_OBJECT_GLOBAL) {
255    Key("Global");
256    LogStr("true");
257    Next();
258  }
259
260  Key("RefCount");
261  LogUint(object->type_flags_refs >> REF_CNT_SHIFT);
262  Next();
263
264  if (ecma_is_lexical_environment(object)) {
265    Type("LexEnv");
266    DumpInfoLexEnv(object);
267    goto finish;
268  } else {
269    Key("__proto__");
270    jmem_cpointer_t proto_cp = object->u2.prototype_cp;
271    if (proto_cp != JMEM_CP_NULL) {
272      LogAddr(ECMA_GET_NON_NULL_POINTER (ecma_object_t, proto_cp));
273    } else {
274      LogAddr(NULL);
275    }
276    Next();
277    switch (ecma_get_object_type(object)) {
278      case ECMA_OBJECT_TYPE_EXTERNAL_FUNCTION: {
279        Type("ExternalFunction");
280        break;
281      }
282      case ECMA_OBJECT_TYPE_BOUND_FUNCTION: {
283        Type("BoundFunction");
284        break;
285      }
286      case ECMA_OBJECT_TYPE_FUNCTION: {
287        Type("Function");
288        DumpInfoFunction(object);
289        Next();
290        break;
291      }
292      case ECMA_OBJECT_TYPE_PSEUDO_ARRAY: {
293        Type("PseudoArray");
294        ecma_extended_object_t* ext_object = (ecma_extended_object_t*)object;
295        if (ext_object->u.pseudo_array.type == ECMA_PSEUDO_ARRAY_ARGUMENTS) {
296          Key("subtype");
297          LogStr("arguments");
298          Next();
299          ecma_object_t* lex_env = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t,
300              ext_object->u.pseudo_array.u2.lex_env_cp);
301          Key("lex_env");
302          LogAddr(lex_env);
303        } else {
304          Key("subtype");
305          LogStr("!!! Unknown");
306        }
307        goto finish;
308      }
309      case ECMA_OBJECT_TYPE_ARRAY: {
310        Type("Array");
311        ecma_extended_object_t* ext_object = (ecma_extended_object_t*)object;
312
313        if (ecma_op_object_is_fast_array(object)) {
314          Key("subtype");
315          LogStr("fast");
316          Next();
317
318          Key("data");
319          Start();
320          if (object->u1.property_list_cp != JMEM_CP_NULL) {
321            ecma_value_t *values =
322                ECMA_GET_NON_NULL_POINTER (ecma_value_t, object->u1.property_list_cp);
323            bool skip_comma = true;
324            for (uint32_t i = 0; i < ext_object->u.array.length; i++) {
325              if (ecma_is_value_array_hole(values[i])) {
326                continue;
327              }
328              if (skip_comma) {
329                skip_comma = false;
330              } else {
331                Next();
332              }
333              KeyUint(i);
334              if (ecma_is_value_object(values[i])) {
335                ecma_object_t* value_obj = ecma_get_object_from_value(values[i]);
336                LogAddr(value_obj);
337              } else {
338                ecma_string_t* value_str = ecma_op_to_string(values[i]);
339                LogStrObj(value_str);
340              }
341            }
342          }
343          End();
344          goto finish;
345        } else {
346          Key("subtype");
347          LogStr("sparse");
348          Next();
349        }
350        break;
351      }
352      default: {
353        Type("Object");
354        break;
355      }
356    }
357  }
358
359  jmem_cpointer_t prop_iter_cp = object->u1.property_list_cp;
360
361#if ENABLED (JERRY_PROPRETY_HASHMAP)
362  if (prop_iter_cp != JMEM_CP_NULL) {
363    ecma_property_header_t* prop_iter_p =
364        ECMA_GET_NON_NULL_POINTER(ecma_property_header_t, prop_iter_cp);
365    if (prop_iter_p->types[0] == ECMA_PROPERTY_TYPE_HASHMAP) {
366      prop_iter_cp = prop_iter_p->next_property_cp;
367    }
368  }
369#endif
370
371  Key("properties");
372  StartList();
373  while (prop_iter_cp != JMEM_CP_NULL) {
374    ecma_property_header_t* prop_iter_p =
375        ECMA_GET_NON_NULL_POINTER(ecma_property_header_t, prop_iter_cp);
376    DumpPropertyPair((ecma_property_pair_t *) prop_iter_p);
377
378    prop_iter_cp = prop_iter_p->next_property_cp;
379    if (prop_iter_cp != JMEM_CP_NULL) {
380      Next();
381    }
382  }
383  EndList();
384
385 finish:
386  End();
387  Next();
388}
389