Skip to content

Commit 5425dbd

Browse files
committed
Add funcitonality to take heap snapshots
Signed-off-by: Ádám László Kulcsár <kuladam@inf.u-szeged.hu>
1 parent 17bdb07 commit 5425dbd

File tree

2 files changed

+370
-0
lines changed

2 files changed

+370
-0
lines changed

src/debugger/Debugger.cpp

Lines changed: 369 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,108 @@
2828
#include "runtime/SandBox.h"
2929
#include "parser/Script.h"
3030

31+
#include <cstdio>
32+
#include "rapidjson/document.h"
33+
#include "rapidjson/filewritestream.h"
34+
#include "rapidjson/writer.h"
35+
#include "rapidjson/prettywriter.h"
36+
3137
#ifdef ESCARGOT_DEBUGGER
3238
namespace Escargot {
3339

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+
34133
void Debugger::enable(Context* context)
35134
{
36135
ASSERT(m_context == nullptr);
@@ -779,6 +878,173 @@ static LexicalEnvironment* getFunctionLexEnv(ExecutionState* state)
779878
return nullptr;
780879
}
781880

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+
7821048
bool DebuggerRemote::processEvents(ExecutionState* state, Optional<ByteCodeBlock*> byteCodeBlock, bool isBlockingRequest)
7831049
{
7841050
uint8_t buffer[ESCARGOT_DEBUGGER_MAX_MESSAGE_LENGTH];
@@ -1005,6 +1271,109 @@ bool DebuggerRemote::processEvents(ExecutionState* state, Optional<ByteCodeBlock
10051271
m_waitForResume = false;
10061272
return true;
10071273
}
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+
}
10081377
}
10091378

10101379
ESCARGOT_LOG_ERROR("Invalid message received. Closing connection.\n");

0 commit comments

Comments
 (0)