diff --git a/src/llnode.cc b/src/llnode.cc index d24fdc4c..204ac64d 100644 --- a/src/llnode.cc +++ b/src/llnode.cc @@ -136,7 +136,8 @@ bool BacktraceCmd::DoExecute(SBDebugger d, char** cmd, lldb::SBMemoryRegionInfo info; if (target.GetProcess().GetMemoryRegionInfo(pc, info).Success() && info.IsExecutable() && info.IsWritable()) { - result.Printf(" %c frame #%u: 0x%016" PRIx64 " <builtin>\n", star, i, pc); + result.Printf(" %c frame #%u: 0x%016" PRIx64 " <builtin>\n", star, i, + pc); continue; } } 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(), "<Second>", 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(), "<Actual>", 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 d36cec53..ff68c812 100644 --- a/src/llv8-constants.cc +++ b/src/llv8-constants.cc @@ -334,6 +334,7 @@ void String::Load() { kConsStringTag = LoadConstant("ConsStringTag"); kSlicedStringTag = LoadConstant("SlicedStringTag"); kExternalStringTag = LoadConstant("ExternalStringTag"); + kThinStringTag = LoadConstant("ThinStringTag"); kLengthOffset = LoadConstant("class_String__length__SMI"); } @@ -362,6 +363,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"); @@ -518,6 +522,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 04a46d23..3cc4e273 100644 --- a/src/llv8-constants.h +++ b/src/llv8-constants.h @@ -248,6 +248,7 @@ class String : public Module { int64_t kConsStringTag; int64_t kSlicedStringTag; int64_t kExternalStringTag; + int64_t kThinStringTag; int64_t kLengthOffset; @@ -297,6 +298,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); @@ -441,6 +452,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 47281eea..8d4a0d51 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 20ecb06d..ec18c305 100644 --- a/src/llv8.cc +++ b/src/llv8.cc @@ -2,7 +2,6 @@ #include <algorithm> #include <cinttypes> -#include <algorithm> #include "llv8-inl.h" #include "llv8.h" @@ -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); @@ -90,8 +90,7 @@ double LLV8::LoadDouble(int64_t addr, Error& err) { std::string LLV8::LoadBytes(int64_t length, int64_t addr, Error& err) { uint8_t* buf = new uint8_t[length + 1]; SBError sberr; - process_.ReadMemory(addr, buf, - static_cast<size_t>(length), sberr); + process_.ReadMemory(addr, buf, static_cast<size_t>(length), sberr); if (sberr.Fail()) { err = Error::Failure("Failed to load V8 raw buffer"); delete[] buf; @@ -268,6 +267,8 @@ std::string JSFrame::Inspect(bool with_args, Error& err) { return "<internal>"; } else if (value == v8()->frame()->kConstructFrame) { return "<constructor>"; + } else if (value == v8()->frame()->kStubFrame) { + return "<stub>"; } else if (value != v8()->frame()->kJSFrame && value != v8()->frame()->kOptimizedFrame) { err = Error::Failure("Unknown frame marker"); @@ -957,6 +958,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(); } @@ -1111,9 +1117,9 @@ std::string JSArrayBuffer::Inspect(InspectOptions* options, Error& err) { char tmp[128]; snprintf(tmp, sizeof(tmp), - "<ArrayBuffer: backingStore=0x%016" PRIx64 ", byteLength=%d", - data, byte_length); - + "<ArrayBuffer: backingStore=0x%016" PRIx64 ", byteLength=%d", data, + byte_length); + std::string res; res += tmp; if (options->detailed) { @@ -1156,7 +1162,8 @@ std::string JSArrayBufferView::Inspect(InspectOptions* options, Error& err) { int byte_offset = static_cast<int>(off.GetValue()); char tmp[128]; snprintf(tmp, sizeof(tmp), - "<ArrayBufferView: backingStore=0x%016" PRIx64 ", byteOffset=%d, byteLength=%d", + "<ArrayBufferView: backingStore=0x%016" PRIx64 + ", byteOffset=%d, byteLength=%d", data, byte_offset, byte_length); std::string res; diff --git a/src/llv8.h b/src/llv8.h index 0e27fcbb..cd11b158 100644 --- a/src/llv8.h +++ b/src/llv8.h @@ -221,6 +221,15 @@ class SlicedString : public String { inline std::string ToString(Error& err); }; +class ThinString : public String { + public: + V8_VALUE_DEFAULT_METHODS(ThinString, String) + + inline String Actual(Error& err); + + inline std::string ToString(Error& err); +}; + class HeapNumber : public HeapObject { public: V8_VALUE_DEFAULT_METHODS(HeapNumber, HeapObject) @@ -405,7 +414,6 @@ class JSArrayBuffer : public HeapObject { inline bool WasNeutered(Error& err); std::string Inspect(InspectOptions* options, Error& err); - }; class JSArrayBufferView : public HeapObject { @@ -477,6 +485,7 @@ class LLV8 { constants::TwoByteString two_byte_string; constants::ConsString cons_string; constants::SlicedString sliced_string; + constants::ThinString thin_string; constants::FixedArrayBase fixed_array_base; constants::FixedArray fixed_array; constants::Oddball oddball; @@ -503,6 +512,7 @@ class LLV8 { friend class TwoByteString; friend class ConsString; friend class SlicedString; + friend class ThinString; friend class HeapNumber; friend class JSObject; friend class JSArray; diff --git a/test/fixtures/inspect-scenario.js b/test/fixtures/inspect-scenario.js index e53a1ee6..4c61e316 100644 --- a/test/fixtures/inspect-scenario.js +++ b/test/fixtures/inspect-scenario.js @@ -8,6 +8,13 @@ let outerVar = 'outer variable'; exports.holder = {}; +function makeThin(a, b) { + var str = a + b; + var obj = {}; + obj[str]; // Turn the cons string into a thin string. + return str; +} + function closure() { function Class() { @@ -28,6 +35,9 @@ function closure() { c.hashmap['cons-string'] = 'this could be a bit smaller, but v8 wants big str.'; c.hashmap['cons-string'] += c.hashmap['cons-string']; + c.hashmap['internalized-string'] = 'foobar'; + // This thin string points to the previous 'foobar'. + c.hashmap['thin-string'] = makeThin('foo', 'bar'); c.hashmap['array'] = [true, 1, undefined, null, 'test', Class]; c.hashmap['long-array'] = new Array(20).fill(5); c.hashmap['array-buffer'] = new Uint8Array( diff --git a/test/frame-test.js b/test/frame-test.js index 69861344..f4f047fc 100644 --- a/test/frame-test.js +++ b/test/frame-test.js @@ -18,7 +18,7 @@ tape('v8 stack', (t) => { // FIXME(bnoordhuis) This can fail with versions of lldb that don't // support the GetMemoryRegions() API; llnode won't be able to identify // V8 builtins stack frames, it just prints them as anonymous frames. - lines = lines.filter((s) => !/<builtin>/.test(s)); + lines = lines.filter((s) => !/<builtin>|<stub>/.test(s)); const eyecatcher = lines[0]; const adapter = lines[1]; const crasher = lines[2]; diff --git a/test/inspect-test.js b/test/inspect-test.js index 657cd1a4..0fe2bdc0 100644 --- a/test/inspect-test.js +++ b/test/inspect-test.js @@ -47,6 +47,7 @@ tape('v8 inspect', (t) => { let regexp = null; let cons = null; + let thin = null; let arrowFunc = null; let array = null; let longArray = null; @@ -112,6 +113,11 @@ tape('v8 inspect', (t) => { t.ok(consMatch, '.cons-string ConsString property'); cons = consMatch[1]; + const thinMatch = lines.match( + /.thin-string=(0x[0-9a-f]+):<String: "foobar">/); + 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]+:<String: "foobar">/.test(lines), + 'thin string content'); + sess.send(`v8 inspect ${array}`); });