diff --git a/README.md b/README.md index 1552d8a7..af300e73 100644 --- a/README.md +++ b/README.md @@ -212,7 +212,7 @@ v8 bt command. See the [Commands](#commands) section below for more commands. ### Commands ``` -(lldb) v8 help +(llnode) v8 help Node.js helpers Syntax: v8 @@ -226,7 +226,7 @@ The following subcommands are supported: Syntax: v8 bt [number] findjsinstances -- List all objects which share the specified map. Accepts the same options as `v8 inspect` - findjsobjects -- List all object types and instance counts grouped by map and sorted by instance count. + findjsobjects -- List all object types and instance counts grouped by typename and sorted by instance count. Requires `LLNODE_RANGESFILE` environment variable to be set to a file containing memory ranges for the core file being debugged. There are scripts for generating this file on Linux and Mac in the scripts directory of the llnode @@ -238,10 +238,7 @@ The following subcommands are supported: * -v, --value expr - all properties that refer to the specified JavaScript object (default) * -n, --name name - all properties with the specified name * -s, --string string - all properties that refer to the specified JavaScript string value - * --array-length num - print maximum of `num` elements in array - getactivehandles -- *EXPERIMENTAL* Equivalent to running process._getActiveHandles. This command is still being developed and for now it only works building node from source. - getactiverequests -- *EXPERIMENTAL* Equivalent to running process._getActiveRequests. This command is still being developed and for now it only works building node from source. inspect -- Print detailed description and contents of the JavaScript value. Possible flags (all optional): @@ -249,7 +246,7 @@ The following subcommands are supported: * -F, --full-string - print whole string without adding ellipsis * -m, --print-map - print object's map address * -s, --print-source - print source code for function objects - * --string-length num - print maximum of `num` characters in string + * -l num, --length num - print maximum of `num` elements from string/array Syntax: v8 inspect [flags] expr nodeinfo -- Print information about Node.js @@ -257,6 +254,9 @@ The following subcommands are supported: Syntax: v8 print expr source -- Source code information + getactivehandles -- *EXPERIMENTAL* Equivalent to running process._getActiveHandles. This command is still being developed and for now it only works building node from source. + getactiverequests -- *EXPERIMENTAL* Equivalent to running process._getActiveRequests. This command is still being developed and for now it only works building node from source. + For more help on any particular subcommand, type 'help '. ``` diff --git a/package.json b/package.json index e60a6948..072d0edd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "llnode", - "version": "1.6.0", + "version": "1.6.1", "description": "Node.js plugin for LLDB", "main": "no-entry-sorry.js", "directories": { diff --git a/src/llnode.cc b/src/llnode.cc index 4432c762..88e240b6 100644 --- a/src/llnode.cc +++ b/src/llnode.cc @@ -46,8 +46,9 @@ char** CommandBase::ParseInspectOptions(char** cmd, v8::Value::InspectOptions* options) { static struct option opts[] = { {"full-string", no_argument, nullptr, 'F'}, - {"string-length", required_argument, nullptr, 0x1001}, - {"array-length", required_argument, nullptr, 0x1002}, + {"string-length", required_argument, nullptr, 'l'}, + {"array-length", required_argument, nullptr, 'l'}, + {"length", required_argument, nullptr, 'l'}, {"print-map", no_argument, nullptr, 'm'}, {"print-source", no_argument, nullptr, 's'}, {nullptr, 0, nullptr, 0}}; @@ -67,21 +68,18 @@ char** CommandBase::ParseInspectOptions(char** cmd, optind = 0; opterr = 1; do { - int arg = getopt_long(argc, args, "Fms", opts, nullptr); + int arg = getopt_long(argc, args, "Fmsl:", opts, nullptr); if (arg == -1) break; switch (arg) { case 'F': - options->string_length = 0; + options->length = 0; break; case 'm': options->print_map = true; break; - case 0x1001: - options->string_length = strtol(optarg, nullptr, 10); - break; - case 0x1002: - options->array_length = strtol(optarg, nullptr, 10); + case 'l': + options->length = strtol(optarg, nullptr, 10); break; case 's': options->print_source = true; @@ -431,8 +429,8 @@ bool PluginInitialize(SBDebugger d) { " * -F, --full-string - print whole string without adding ellipsis\n" " * -m, --print-map - print object's map address\n" " * -s, --print-source - print source code for function objects\n" - " * --string-length num - print maximum of `num` characters in string\n" - " * --array-length num - print maximum of `num` elements in array\n" + " * -l num, --length num - print maximum of `num` elements from " + "string/array\n" "\n" "Syntax: v8 inspect [flags] expr\n"); interpreter.AddCommand("jsprint", new llnode::PrintCmd(true), @@ -447,8 +445,8 @@ bool PluginInitialize(SBDebugger d) { "Alias for `v8 source list`"); v8.AddCommand("findjsobjects", new llnode::FindObjectsCmd(), - "List all object types and instance counts grouped by map and " - "sorted by instance count.\n" + "List all object types and instance counts grouped by type" + "name and sorted by instance count.\n" #ifndef LLDB_SBMemoryRegionInfoList_h_ "Requires `LLNODE_RANGESFILE` environment variable to be set " "to a file containing memory ranges for the core file being " diff --git a/src/llscan.cc b/src/llscan.cc index 9ebc3e94..63988e18 100644 --- a/src/llscan.cc +++ b/src/llscan.cc @@ -617,6 +617,14 @@ void FindReferencesCmd::ReferenceScanner::PrintRefs( result.Printf("0x%" PRIx64 ": %s.%s=0x%" PRIx64 "\n", str.raw(), type_name.c_str(), "", search_value_.raw()); } + } else if (repr == v8->string()->kThinStringTag) { + v8::ThinString thin_str(str); + v8::String actual = thin_str.Actual(err); + if (err.Success() && actual.raw() == search_value_.raw()) { + std::string type_name = thin_str.GetTypeName(err); + result.Printf("0x%" PRIx64 ": %s.%s=0x%" PRIx64 "\n", str.raw(), + type_name.c_str(), "", search_value_.raw()); + } } // Nothing to do for other kinds of string. } @@ -694,6 +702,14 @@ void FindReferencesCmd::ReferenceScanner::ScanRefs(v8::String& str, references = llscan.GetReferencesByValue(second.raw()); references->push_back(str.raw()); } + } else if (repr == v8->string()->kThinStringTag) { + v8::ThinString thin_str(str); + v8::String actual = thin_str.Actual(err); + + if (err.Success()) { + references = llscan.GetReferencesByValue(actual.raw()); + references->push_back(str.raw()); + } } // Nothing to do for other kinds of string. } diff --git a/src/llv8-constants.cc b/src/llv8-constants.cc index 00f7e760..2faada99 100644 --- a/src/llv8-constants.cc +++ b/src/llv8-constants.cc @@ -337,6 +337,7 @@ void String::Load() { kConsStringTag = LoadConstant("ConsStringTag"); kSlicedStringTag = LoadConstant("SlicedStringTag"); kExternalStringTag = LoadConstant("ExternalStringTag"); + kThinStringTag = LoadConstant("ThinStringTag"); kLengthOffset = LoadConstant("class_String__length__SMI"); } @@ -365,6 +366,9 @@ void SlicedString::Load() { kOffsetOffset = LoadConstant("class_SlicedString__offset__SMI"); } +void ThinString::Load() { + kActualOffset = LoadConstant("class_ThinString__actual__String"); +} void FixedArrayBase::Load() { kLengthOffset = LoadConstant("class_FixedArrayBase__length__SMI"); @@ -521,6 +525,7 @@ void Frame::Load() { kConstructFrame = LoadConstant("frametype_ConstructFrame"); kJSFrame = LoadConstant("frametype_JavaScriptFrame"); kOptimizedFrame = LoadConstant("frametype_OptimizedFrame"); + kStubFrame = LoadConstant("frametype_StubFrame"); } diff --git a/src/llv8-constants.h b/src/llv8-constants.h index 3e6b84a9..c24a287c 100644 --- a/src/llv8-constants.h +++ b/src/llv8-constants.h @@ -256,6 +256,7 @@ class String : public Module { int64_t kConsStringTag; int64_t kSlicedStringTag; int64_t kExternalStringTag; + int64_t kThinStringTag; int64_t kLengthOffset; @@ -305,6 +306,16 @@ class SlicedString : public Module { void Load(); }; +class ThinString : public Module { + public: + MODULE_DEFAULT_METHODS(ThinString); + + int64_t kActualOffset; + + protected: + void Load(); +}; + class FixedArrayBase : public Module { public: MODULE_DEFAULT_METHODS(FixedArrayBase); @@ -449,6 +460,7 @@ class Frame : public Module { int64_t kConstructFrame; int64_t kJSFrame; int64_t kOptimizedFrame; + int64_t kStubFrame; protected: void Load(); diff --git a/src/llv8-inl.h b/src/llv8-inl.h index 3c633021..60ddb242 100644 --- a/src/llv8-inl.h +++ b/src/llv8-inl.h @@ -272,6 +272,8 @@ ACCESSOR(ConsString, Second, cons_string()->kSecondOffset, String); ACCESSOR(SlicedString, Parent, sliced_string()->kParentOffset, String); ACCESSOR(SlicedString, Offset, sliced_string()->kOffsetOffset, Smi); +ACCESSOR(ThinString, Actual, thin_string()->kActualOffset, String); + ACCESSOR(FixedArrayBase, Length, fixed_array_base()->kLengthOffset, Smi); inline std::string OneByteString::ToString(Error& err) { @@ -319,6 +321,16 @@ inline std::string SlicedString::ToString(Error& err) { return tmp.substr(offset.GetValue(), length.GetValue()); } +inline std::string ThinString::ToString(Error& err) { + String actual = Actual(err); + if (err.Fail()) return std::string(); + + std::string tmp = actual.ToString(err); + if (err.Fail()) return std::string(); + + return tmp; +} + inline int64_t FixedArray::LeaData() const { return LeaField(v8()->fixed_array()->kDataOffset); } diff --git a/src/llv8.cc b/src/llv8.cc index a7014a85..bba18982 100644 --- a/src/llv8.cc +++ b/src/llv8.cc @@ -1,6 +1,5 @@ #include -#include #include #include @@ -43,6 +42,7 @@ void LLV8::Load(SBTarget target) { two_byte_string.Assign(target, &common); cons_string.Assign(target, &common); sliced_string.Assign(target, &common); + thin_string.Assign(target, &common); fixed_array_base.Assign(target, &common); fixed_array.Assign(target, &common); oddball.Assign(target, &common); @@ -269,6 +269,8 @@ std::string JSFrame::Inspect(bool with_args, Error& err) { return ""; } else if (value == v8()->frame()->kConstructFrame) { return ""; + } else if (value == v8()->frame()->kStubFrame) { + return ""; } else if (value != v8()->frame()->kJSFrame && value != v8()->frame()->kOptimizedFrame) { err = Error::Failure("Unknown frame marker"); @@ -958,6 +960,11 @@ std::string String::ToString(Error& err) { return std::string("(external)"); } + if (repr == v8()->string()->kThinStringTag) { + ThinString thin(this); + return thin.ToString(err); + } + err = Error::Failure("Unsupported string representation"); return std::string(); } @@ -967,7 +974,7 @@ std::string String::Inspect(InspectOptions* options, Error& err) { std::string val = ToString(err); if (err.Fail()) return std::string(); - unsigned int len = options->string_length; + unsigned int len = options->length; if (len != 0 && val.length() > len) val = val.substr(0, len) + "..."; @@ -1120,7 +1127,7 @@ std::string JSArrayBuffer::Inspect(InspectOptions* options, Error& err) { if (options->detailed) { res += ": [\n "; - int display_length = std::min(byte_length, options->array_length); + int display_length = std::min(byte_length, options->length); res += v8()->LoadBytes(display_length, data, err); if (display_length < byte_length) { @@ -1156,8 +1163,9 @@ std::string JSArrayBufferView::Inspect(InspectOptions* options, Error& err) { int byte_length = static_cast(length.GetValue()); int byte_offset = static_cast(off.GetValue()); char tmp[128]; - snprintf(tmp, sizeof(tmp), "detailed) { res += ": [\n "; - int display_length = std::min(byte_length, options->array_length); + int display_length = std::min(byte_length, options->length); res += v8()->LoadBytes(display_length, data + byte_offset, err); if (display_length < byte_length) { @@ -1915,7 +1923,7 @@ std::string JSArray::Inspect(InspectOptions* options, Error& err) { std::string res = "/); + t.ok(thinMatch, '.thin-string ThinString property'); + thin = thinMatch[1]; + sess.send(`v8 inspect ${regexp}`); sess.send(`v8 inspect -F ${cons}`); }); @@ -141,6 +147,15 @@ tape('v8 inspect', (t) => { -1, '--string-length truncates the string'); + sess.send(`v8 inspect ${thin}`); + }); + + sess.linesUntil(/">/, (lines) => { + lines = lines.join('\n'); + t.ok( + /0x[0-9a-f]+:/.test(lines), + 'thin string content'); + sess.send(`v8 inspect ${array}`); });