/* * Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved. * Description: add ref tracker. * Create: 2020/11/20 */ #include "tracker.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ext-utils.h" #include "heapdump.h" #include "vm.h" #if defined(JERRY_REF_TRACKER) #define BUFFER_SIZE 65536 #define LogTracker(...) do { fprintf(gLogTrackerFile, __VA_ARGS__); } while (0) bool gNativeManip = false; // This variable shall be used only in "streaming" mode. bool gRefTrackerEnabled = false; static Storage gStorages[2]; static FILE* gLogTrackerFile = NULL; bool GetRefTrackerEnabled(void) { return gRefTrackerEnabled; } void SetRefTrackerEnabled(bool flag) { gRefTrackerEnabled = flag; } void LogTrackerInit(const char* filepath) { gLogTrackerFile = fopen(filepath, "w+"); } void LogTrackerClose(void) { fclose(gLogTrackerFile); } void RefInfoResetFn(IStorageItem* item) { RefInfoItem* ref_item = (RefInfoItem*)item; JerryExtFree(ref_item->stacktrace); JerryExtFree(ref_item); } void InitTracker(void) { StorageInit(&gStorages[kObjectRefInfo], BUFFER_SIZE); StorageInit(&gStorages[kStringRefInfo], BUFFER_SIZE); } void StorageInit(Storage* storage, size_t size) { storage->size = size; storage->data = JerryExtAllocStorageData(size); } void StoragePut(Storage* storage, size_t key, size_t value) { if (storage->data[key] == 0) { storage->data[key] = value; return; } IStorageItem* item = (IStorageItem*)storage->data[key]; storage->data[key] = value; IStorageItem* new_item = (IStorageItem*)value; new_item->next = item; } size_t StorageGet(Storage* storage, size_t key) { return storage->data[key]; } void StorageReset(Storage* storage, size_t key, void(*reset_fn)(IStorageItem*)) { if (storage->data[key] == 0) { // Nothing to reset. return; } IStorageItem* item = (IStorageItem*)storage->data[key]; storage->data[key] = 0; while (item != NULL) { IStorageItem* next = item->next; reset_fn(item); item = next; } } char* GetStacktraceString(ecma_string_t* stack) { if (stack == NULL) { return NULL; } // Copy string to jsheap-independent storage. ECMA_STRING_TO_UTF8_STRING(stack, data, data_size); char* dst = JerryExtAllocStr(data_size + 1); memcpy(dst, data, data_size); dst[data_size] = '\0'; return dst; } RefInfoItem* CreateRefInfoItem(RefInfoItemFlags flags, char* stack_str) { RefInfoItem* item = JerryExtAlloc(sizeof(RefInfoItem)); item->next = NULL; item->stacktrace = stack_str; item->flags = flags; return item; } void ReportObjRefManip(ecma_object_t* obj, RefInfoItemFlags flags) { if (!gRefTrackerEnabled) { return; } if (gNativeManip) { flags &= kRefNative; } // XXX: For now don't do anything regarding within-gc ops. if (flags & (kRefMark | kRefUnmark)) { return; } const char* native_str = " native"; const char* init_str = " init"; const char* inc_str = " increment"; const char* dec_str = " decrement"; uint32_t refcnt = obj->type_flags_refs >> REF_CNT_SHIFT; LogTracker("Object %p%s%s%s%s: refcount = %u\n", (void*)obj, (flags & kRefNative) ? native_str : "", (flags & kRefInit) ? init_str : "", (flags & kRefRef) ? inc_str : "", (flags & kRefDeref) ? dec_str : "", refcnt); LogTracker("\n"); } void ReportObjDelete(ecma_object_t* obj) { if (!gRefTrackerEnabled) { return; } LogTracker("Object %p deleted\n", (void*)obj); LogTracker("\n"); } void DumpTracker(void) { Storage* storage = &gStorages[kObjectRefInfo]; for (int i = 0; i < (int)storage->size; ++i) { if (storage->data[i] != 0) { RefInfoItem* item = (RefInfoItem*)storage->data[i]; printf("--== %p ==--:\n", (void*)ECMA_OBJECT_FROM_STORAGE_KEY(i)); while (item != NULL) { printf("flags = %d\nStacktrace:\n%s\n", item->flags, item->stacktrace); item = item->next; } } } } #endif