|
28 | 28 | #include "runtime/SandBox.h" |
29 | 29 | #include "parser/Script.h" |
30 | 30 |
|
| 31 | +#include <cstdio> |
| 32 | +#include "rapidjson/document.h" |
| 33 | +#include "rapidjson/filewritestream.h" |
| 34 | +#include "rapidjson/writer.h" |
| 35 | +#include "rapidjson/prettywriter.h" |
| 36 | + |
31 | 37 | #ifdef ESCARGOT_DEBUGGER |
32 | 38 | namespace Escargot { |
33 | 39 |
|
| 40 | + |
| 41 | +#define NODE_FIELD_COUNT 6 |
| 42 | +struct snapshotNode { |
| 43 | + enum nodeType { |
| 44 | + hidden, |
| 45 | + array, |
| 46 | + string, |
| 47 | + object, |
| 48 | + code, |
| 49 | + closure, |
| 50 | + regexp, |
| 51 | + number, |
| 52 | + native, |
| 53 | + synthetic, |
| 54 | + concatenated_string, |
| 55 | + sliced_string, |
| 56 | + symbol, |
| 57 | + bigint, |
| 58 | + object_shape |
| 59 | + }; |
| 60 | + |
| 61 | + snapshotNode(nodeType type, uint64_t name_idx, uint64_t id, uint64_t self_size, bool detachedness) |
| 62 | + : m_type(type) |
| 63 | + , m_name_idx(name_idx) |
| 64 | + , m_id(id) |
| 65 | + , m_self_size(self_size) |
| 66 | + , m_edge_count(0) |
| 67 | + , m_detachedness(detachedness) |
| 68 | + { |
| 69 | + } |
| 70 | + |
| 71 | + nodeType m_type; |
| 72 | + uint64_t m_name_idx; |
| 73 | + uint64_t m_id; |
| 74 | + uint64_t m_self_size; |
| 75 | + uint64_t m_edge_count; |
| 76 | + // if the object can be reached from the global env |
| 77 | + bool m_detachedness; |
| 78 | +}; |
| 79 | + |
| 80 | +// /"edge_types":[["context","element","property","internal","hidden","shortcut","weak"] |
| 81 | +struct snapshotEdge { |
| 82 | + enum edgeType { |
| 83 | + context, |
| 84 | + element, |
| 85 | + property, |
| 86 | + internal, |
| 87 | + hidden, |
| 88 | + shortcut, |
| 89 | + weak, |
| 90 | + }; |
| 91 | + |
| 92 | + snapshotEdge(edgeType type, uint64_t str_idx, uint64_t to_node_idx) |
| 93 | + : m_type(type) |
| 94 | + , m_str_idx(str_idx) |
| 95 | + , m_to_node_idx(to_node_idx) |
| 96 | + { |
| 97 | + } |
| 98 | + |
| 99 | + edgeType m_type; |
| 100 | + uint64_t m_str_idx; |
| 101 | + uint64_t m_to_node_idx; |
| 102 | +}; |
| 103 | + |
| 104 | +struct snapshotInfo { |
| 105 | + std::vector<snapshotNode> nodes; |
| 106 | + std::vector<snapshotEdge> edges; |
| 107 | + std::vector<std::string> strings; |
| 108 | +}; |
| 109 | + |
| 110 | +uint64_t addNode(snapshotInfo& info, snapshotNode::nodeType type, const std::string& name, uint64_t* id, uint64_t size, bool detachedness) |
| 111 | +{ |
| 112 | + info.nodes.push_back(snapshotNode( |
| 113 | + type, |
| 114 | + info.strings.size(), |
| 115 | + *id, |
| 116 | + size, |
| 117 | + detachedness)); |
| 118 | + info.strings.push_back(name); |
| 119 | + *id += 1; |
| 120 | + |
| 121 | + return info.nodes.size() - 1; |
| 122 | +} |
| 123 | + |
| 124 | +void addEdge(snapshotInfo& info, snapshotEdge::edgeType type, uint64_t from, uint64_t to) |
| 125 | +{ |
| 126 | + info.edges.push_back(snapshotEdge( |
| 127 | + type, |
| 128 | + info.nodes[from].m_name_idx, |
| 129 | + to * NODE_FIELD_COUNT)); |
| 130 | + info.nodes[from].m_edge_count++; |
| 131 | +} |
| 132 | + |
34 | 133 | void Debugger::enable(Context* context) |
35 | 134 | { |
36 | 135 | ASSERT(m_context == nullptr); |
@@ -779,6 +878,173 @@ static LexicalEnvironment* getFunctionLexEnv(ExecutionState* state) |
779 | 878 | return nullptr; |
780 | 879 | } |
781 | 880 |
|
| 881 | +void prepareHeapsnapshotFile(snapshotInfo* infos) |
| 882 | +{ |
| 883 | + rapidjson::Document jsonDoc; |
| 884 | + rapidjson::Document::AllocatorType& allocator = jsonDoc.GetAllocator(); |
| 885 | + jsonDoc.SetObject(); |
| 886 | + |
| 887 | + rapidjson::Value meta(rapidjson::kObjectType); |
| 888 | + |
| 889 | + rapidjson::Value node_fields(rapidjson::kArrayType); |
| 890 | + { |
| 891 | + node_fields.PushBack("type", allocator); |
| 892 | + node_fields.PushBack("name", allocator); |
| 893 | + node_fields.PushBack("id", allocator); |
| 894 | + node_fields.PushBack("self_size", allocator); |
| 895 | + node_fields.PushBack("edge_count", allocator); |
| 896 | + node_fields.PushBack("detachedness", allocator); |
| 897 | + meta.AddMember("node_fields", node_fields, allocator); |
| 898 | + } |
| 899 | + |
| 900 | + rapidjson::Value types(rapidjson::kArrayType); |
| 901 | + { |
| 902 | + types.PushBack("hidden", allocator); |
| 903 | + types.PushBack("array", allocator); |
| 904 | + types.PushBack("string", allocator); |
| 905 | + types.PushBack("object", allocator); |
| 906 | + types.PushBack("code", allocator); |
| 907 | + types.PushBack("closure", allocator); |
| 908 | + types.PushBack("regexp", allocator); |
| 909 | + types.PushBack("number", allocator); |
| 910 | + types.PushBack("native", allocator); |
| 911 | + types.PushBack("synthetic", allocator); |
| 912 | + types.PushBack("concatenated string", allocator); |
| 913 | + types.PushBack("sliced string", allocator); |
| 914 | + types.PushBack("symbol", allocator); |
| 915 | + types.PushBack("bigint", allocator); |
| 916 | + types.PushBack("object shape", allocator); |
| 917 | + } |
| 918 | + |
| 919 | + rapidjson::Value node_types(rapidjson::kArrayType); |
| 920 | + { |
| 921 | + node_types.PushBack(types, allocator); |
| 922 | + node_types.PushBack("string", allocator); |
| 923 | + node_types.PushBack("number", allocator); |
| 924 | + node_types.PushBack("number", allocator); |
| 925 | + node_types.PushBack("number", allocator); |
| 926 | + node_types.PushBack("number", allocator); |
| 927 | + meta.AddMember("node_types", node_types, allocator); |
| 928 | + } |
| 929 | + |
| 930 | + rapidjson::Value edge_fields(rapidjson::kArrayType); |
| 931 | + { |
| 932 | + edge_fields.PushBack("type", allocator); |
| 933 | + edge_fields.PushBack("name_or_index", allocator); |
| 934 | + edge_fields.PushBack("to_node", allocator); |
| 935 | + meta.AddMember("edge_fields", edge_fields, allocator); |
| 936 | + } |
| 937 | + |
| 938 | + rapidjson::Value edge_types_inner(rapidjson::kArrayType); |
| 939 | + { |
| 940 | + edge_types_inner.PushBack("context", allocator); |
| 941 | + edge_types_inner.PushBack("element", allocator); |
| 942 | + edge_types_inner.PushBack("property", allocator); |
| 943 | + edge_types_inner.PushBack("internal", allocator); |
| 944 | + edge_types_inner.PushBack("hidden", allocator); |
| 945 | + edge_types_inner.PushBack("shortcut", allocator); |
| 946 | + edge_types_inner.PushBack("weak", allocator); |
| 947 | + } |
| 948 | + |
| 949 | + rapidjson::Value edge_types(rapidjson::kArrayType); |
| 950 | + { |
| 951 | + edge_types.PushBack(edge_types_inner, allocator); |
| 952 | + edge_types.PushBack("string_or_number", allocator); |
| 953 | + edge_types.PushBack("node", allocator); |
| 954 | + meta.AddMember("edge_types", edge_types, allocator); |
| 955 | + } |
| 956 | + |
| 957 | + // rapidjson::Value trace_function_info_fields(rapidjson::kArrayType); |
| 958 | + // { |
| 959 | + // trace_function_info_fields.PushBack("function_id", allocator); |
| 960 | + // trace_function_info_fields.PushBack("name", allocator); |
| 961 | + // trace_function_info_fields.PushBack("script_name", allocator); |
| 962 | + // trace_function_info_fields.PushBack("script_id", allocator); |
| 963 | + // trace_function_info_fields.PushBack("line", allocator); |
| 964 | + // trace_function_info_fields.PushBack("column", allocator); |
| 965 | + // meta.AddMember("trace_function_info_fields", trace_function_info_fields, allocator); |
| 966 | + // } |
| 967 | + |
| 968 | + // rapidjson::Value trace_node_fields(rapidjson::kArrayType); |
| 969 | + // { |
| 970 | + // trace_node_fields.PushBack("id", allocator); |
| 971 | + // trace_node_fields.PushBack("function_info_index", allocator); |
| 972 | + // trace_node_fields.PushBack("count", allocator); |
| 973 | + // trace_node_fields.PushBack("size", allocator); |
| 974 | + // trace_node_fields.PushBack("children", allocator); |
| 975 | + // meta.AddMember("trace_node_fields", trace_node_fields, allocator); |
| 976 | + // } |
| 977 | + |
| 978 | + // rapidjson::Value sample_fields(rapidjson::kArrayType); |
| 979 | + // { |
| 980 | + // sample_fields.PushBack("timestamp_us", allocator); |
| 981 | + // sample_fields.PushBack("last_assigned_id", allocator); |
| 982 | + // meta.AddMember("sample_fields", sample_fields, allocator); |
| 983 | + // } |
| 984 | + |
| 985 | + rapidjson::Value location_fields(rapidjson::kArrayType); |
| 986 | + { |
| 987 | + location_fields.PushBack("object_index", allocator); |
| 988 | + location_fields.PushBack("script_id", allocator); |
| 989 | + location_fields.PushBack("line", allocator); |
| 990 | + location_fields.PushBack("column", allocator); |
| 991 | + meta.AddMember("location_fields", location_fields, allocator); |
| 992 | + } |
| 993 | + |
| 994 | + rapidjson::Value snapshot(rapidjson::kObjectType); |
| 995 | + { |
| 996 | + snapshot.AddMember("meta", meta, allocator); |
| 997 | + snapshot.AddMember("node_count", infos->nodes.size(), allocator); |
| 998 | + snapshot.AddMember("edge_count", infos->edges.size(), allocator); |
| 999 | + // snapshot.AddMember("trace_function_count", 0, allocator); |
| 1000 | + snapshot.AddMember("extra_native_bytes", 0, allocator); |
| 1001 | + } |
| 1002 | + |
| 1003 | + rapidjson::Value nodes(rapidjson::kArrayType); |
| 1004 | + for (snapshotNode& node : infos->nodes) { |
| 1005 | + nodes.PushBack(node.m_type, allocator); |
| 1006 | + nodes.PushBack(node.m_name_idx, allocator); |
| 1007 | + nodes.PushBack(node.m_id, allocator); |
| 1008 | + nodes.PushBack(node.m_self_size, allocator); |
| 1009 | + nodes.PushBack(node.m_edge_count, allocator); |
| 1010 | + nodes.PushBack((uint32_t)node.m_detachedness, allocator); |
| 1011 | + } |
| 1012 | + |
| 1013 | + rapidjson::Value edges(rapidjson::kArrayType); |
| 1014 | + for (snapshotEdge& edge : infos->edges) { |
| 1015 | + edges.PushBack(edge.m_type, allocator); |
| 1016 | + edges.PushBack(edge.m_str_idx, allocator); |
| 1017 | + edges.PushBack(edge.m_to_node_idx, allocator); |
| 1018 | + } |
| 1019 | + |
| 1020 | + rapidjson::Value locations(rapidjson::kArrayType); |
| 1021 | + rapidjson::Value strings(rapidjson::kArrayType); |
| 1022 | + for (std::string& str : infos->strings) { |
| 1023 | + rapidjson::Value s; |
| 1024 | + s = rapidjson::StringRef(str.c_str()); |
| 1025 | + strings.PushBack(s, allocator); |
| 1026 | + } |
| 1027 | + |
| 1028 | + jsonDoc.AddMember("snapshot", snapshot, allocator); |
| 1029 | + jsonDoc.AddMember("nodes", nodes, allocator); |
| 1030 | + jsonDoc.AddMember("edges", edges, allocator); |
| 1031 | + jsonDoc.AddMember("locations", locations, allocator); |
| 1032 | + jsonDoc.AddMember("strings", strings, allocator); |
| 1033 | + |
| 1034 | + FILE* output = fopen("output.heapsnapshot", "w"); |
| 1035 | + if (output == nullptr) { |
| 1036 | + printf("file creation error!"); |
| 1037 | + abort(); |
| 1038 | + } |
| 1039 | + |
| 1040 | + char buffer[65536]; |
| 1041 | + rapidjson::FileWriteStream os(output, buffer, sizeof(buffer)); |
| 1042 | + rapidjson::Writer<rapidjson::FileWriteStream> writer(os); |
| 1043 | + jsonDoc.Accept(writer); |
| 1044 | + |
| 1045 | + fclose(output); |
| 1046 | +} |
| 1047 | + |
782 | 1048 | bool DebuggerRemote::processEvents(ExecutionState* state, Optional<ByteCodeBlock*> byteCodeBlock, bool isBlockingRequest) |
783 | 1049 | { |
784 | 1050 | uint8_t buffer[ESCARGOT_DEBUGGER_MAX_MESSAGE_LENGTH]; |
@@ -1005,6 +1271,109 @@ bool DebuggerRemote::processEvents(ExecutionState* state, Optional<ByteCodeBlock |
1005 | 1271 | m_waitForResume = false; |
1006 | 1272 | return true; |
1007 | 1273 | } |
| 1274 | + case ESCARGOT_DEBUGGER_TAKE_HEAP_SNAPSHOT: { |
| 1275 | + GC_disable(); |
| 1276 | + |
| 1277 | + uint64_t id = 0; |
| 1278 | + snapshotInfo info; |
| 1279 | + |
| 1280 | + // [["hidden","array","string","object","code","closure","regexp","number","native","synthetic","concatenated string","sliced string","symbol","bigint","object shape"]," |
| 1281 | + // "edge_types":[["context","element","property","internal","hidden","shortcut","weak"] |
| 1282 | + uint64_t stateIdx = addNode(info, snapshotNode::hidden, "state", &id, sizeof(*state), false); |
| 1283 | + |
| 1284 | + uint64_t contextIdx = addNode(info, snapshotNode::hidden, "context", &id, sizeof(*state->context()), false); |
| 1285 | + addEdge(info, snapshotEdge::element, stateIdx, contextIdx); |
| 1286 | + |
| 1287 | + // global lexical environment |
| 1288 | + uint64_t gLexicalEnvIdx = addNode(info, snapshotNode::hidden, "global lexical environment", &id, sizeof(*state->lexicalEnvironment()), false); |
| 1289 | + addEdge(info, snapshotEdge::element, stateIdx, gLexicalEnvIdx); |
| 1290 | + |
| 1291 | + // global env record |
| 1292 | + uint64_t gEnvRecordIdx = addNode(info, |
| 1293 | + snapshotNode::hidden, |
| 1294 | + "global environment record", |
| 1295 | + &id, |
| 1296 | + sizeof(*state->lexicalEnvironment()->record()->asGlobalEnvironmentRecord()), |
| 1297 | + false); |
| 1298 | + addEdge(info, snapshotEdge::element, gLexicalEnvIdx, gEnvRecordIdx); |
| 1299 | + |
| 1300 | + InterpretedCodeBlock* globalCodeBlock = state->lexicalEnvironment()->record()->asGlobalEnvironmentRecord()->globalCodeBlock(); |
| 1301 | + uint64_t gCodeBlock = addNode(info, |
| 1302 | + snapshotNode::code, |
| 1303 | + "global code block", |
| 1304 | + &id, |
| 1305 | + sizeof(*globalCodeBlock), |
| 1306 | + false); |
| 1307 | + addEdge(info, snapshotEdge::element, gLexicalEnvIdx, gCodeBlock); |
| 1308 | + |
| 1309 | + for (uint64_t i = 0; i < globalCodeBlock->children().size(); i++) { |
| 1310 | + uint64_t childBlock = addNode(info, |
| 1311 | + snapshotNode::code, |
| 1312 | + "interpreted code block", |
| 1313 | + &id, |
| 1314 | + sizeof(*globalCodeBlock->children()[i]), |
| 1315 | + false); |
| 1316 | + addEdge(info, snapshotEdge::element, gCodeBlock, childBlock); |
| 1317 | + } |
| 1318 | + |
| 1319 | + GlobalObject* globalObj = state->lexicalEnvironment()->record()->asGlobalEnvironmentRecord()->globalObject(); |
| 1320 | + uint64_t gObj = addNode(info, |
| 1321 | + snapshotNode::object, |
| 1322 | + "global object", |
| 1323 | + &id, |
| 1324 | + sizeof(*globalObj), |
| 1325 | + false); |
| 1326 | + addEdge(info, snapshotEdge::element, gLexicalEnvIdx, gObj); |
| 1327 | + |
| 1328 | + Object::OwnPropertyKeyVector keys = globalObj->ownPropertyKeys(*state); |
| 1329 | + for (size_t i = 0; i < keys.size(); i++) { |
| 1330 | + Value& key = keys[i]; |
| 1331 | + std::string snapshot_str = ""; |
| 1332 | + |
| 1333 | + // [["hidden","array","string","object","code","closure","regexp","number","native","synthetic","concatenated string","sliced string","symbol","bigint","object shape"]," |
| 1334 | + snapshotNode::nodeType type = snapshotNode::hidden; |
| 1335 | + if (key.isNull()) { |
| 1336 | + type = snapshotNode::hidden; |
| 1337 | + snapshot_str = "null"; |
| 1338 | + } else if (key.isTrue() || key.isFalse()) { |
| 1339 | + type = snapshotNode::number; |
| 1340 | + snapshot_str = "boolean"; |
| 1341 | + } else if (key.isNumber()) { |
| 1342 | + type = snapshotNode::number; |
| 1343 | + } else if (key.isString() || key.isSymbol() || key.isBigInt()) { |
| 1344 | + if (key.isString()) { |
| 1345 | + type = snapshotNode::string; |
| 1346 | + snapshot_str = std::string(key.toString(*state)->toUTF8StringData().data(), key.toString(*state)->toUTF8StringData().size()); |
| 1347 | + } else if (key.isBigInt()) { |
| 1348 | + type = snapshotNode::bigint; |
| 1349 | + snapshot_str = "bigint"; |
| 1350 | + } else { |
| 1351 | + type = snapshotNode::symbol; |
| 1352 | + snapshot_str = "symbol"; |
| 1353 | + } |
| 1354 | + } else if (key.isFunction()) { |
| 1355 | + type = snapshotNode::code; |
| 1356 | + snapshot_str = "function"; |
| 1357 | + } else if (key.isObject()) { |
| 1358 | + type = snapshotNode::object; |
| 1359 | + snapshot_str = "object"; |
| 1360 | + } |
| 1361 | + |
| 1362 | + uint64_t property = addNode(info, |
| 1363 | + type, |
| 1364 | + snapshot_str, |
| 1365 | + &id, |
| 1366 | + sizeof(key), |
| 1367 | + false); |
| 1368 | + addEdge(info, snapshotEdge::property, gObj, property); |
| 1369 | + } |
| 1370 | + |
| 1371 | + printf("nodes: %ld | edges: %ld | strings: %ld", info.nodes.size(), info.edges.size(), info.strings.size()); |
| 1372 | + prepareHeapsnapshotFile(&info); |
| 1373 | + |
| 1374 | + GC_enable(); |
| 1375 | + return true; |
| 1376 | + } |
1008 | 1377 | } |
1009 | 1378 |
|
1010 | 1379 | ESCARGOT_LOG_ERROR("Invalid message received. Closing connection.\n"); |
|
0 commit comments