Commit 1cb7aeb9 authored by Z Nguyen-Huu's avatar Z Nguyen-Huu Committed by Commit Bot

[v8windbg] Display js function only for js frame

For js frame, we want to display currently executing function.

Change-Id: If33b04279dafdf6e4834bfb6c7240e8e7e799fc7
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2411483Reviewed-by: 's avatarSeth Brenith <seth.brenith@microsoft.com>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Commit-Queue: Z Nguyen-Huu <duongn@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#70018}
parent a1b8d384
......@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "src/api/api-inl.h"
#include "src/execution/frames-inl.h"
#include "src/flags/flags.h"
#include "src/heap/read-only-spaces.h"
#include "src/heap/spaces.h"
......@@ -34,7 +35,7 @@ class MemoryFailureRegion {
// Implement the memory-reading callback. This one just fetches memory from the
// current process, but a real implementation for a debugging extension would
// fetch memory from the debuggee process or crash dump.
d::MemoryAccessResult ReadMemory(uintptr_t address, uint8_t* destination,
d::MemoryAccessResult ReadMemory(uintptr_t address, void* destination,
size_t byte_count) {
if (address >= memory_fail_start && address <= memory_fail_end) {
// Simulate failure to read debuggee memory.
......@@ -410,5 +411,44 @@ TEST(ListObjectClasses) {
CHECK_NE(class_set.find("v8::internal::JSRegExp"), class_set.end());
}
static void FrameIterationCheck(
v8::Local<v8::String> name,
const v8::PropertyCallbackInfo<v8::Value>& info) {
i::StackFrameIterator iter(reinterpret_cast<i::Isolate*>(info.GetIsolate()));
for (int i = 0; !iter.done(); i++) {
i::StackFrame* frame = iter.frame();
CHECK(i != 0 || (frame->type() == i::StackFrame::EXIT));
d::StackFrameResultPtr props = d::GetStackFrame(frame->fp(), &ReadMemory);
if (frame->is_java_script()) {
JavaScriptFrame* js_frame = JavaScriptFrame::cast(frame);
CHECK_EQ(props->num_properties, 1);
CheckProp(*props->properties[0], "v8::internal::JSFunction",
"currently_executing_jsfunction", js_frame->function().ptr());
} else {
CHECK_EQ(props->num_properties, 0);
}
iter.Advance();
}
}
THREADED_TEST(GetFrameStack) {
LocalContext env;
v8::Isolate* isolate = env->GetIsolate();
v8::HandleScope scope(isolate);
v8::Local<v8::ObjectTemplate> obj = v8::ObjectTemplate::New(isolate);
obj->SetAccessor(v8_str("xxx"), FrameIterationCheck);
CHECK(env->Global()
->Set(env.local(), v8_str("obj"),
obj->NewInstance(env.local()).ToLocalChecked())
.FromJust());
v8::Script::Compile(env.local(), v8_str("function foo() {"
" return obj.xxx;"
"}"
"foo();"))
.ToLocalChecked()
->Run(env.local())
.ToLocalChecked();
}
} // namespace internal
} // namespace v8
......@@ -173,6 +173,37 @@ class ObjectPropertiesResult {
std::vector<const char*> guessed_types_raw_;
};
class StackFrameResult;
struct StackFrameResultExtended : public d::StackFrameResult {
// Back reference for cleanup.
debug_helper_internal::StackFrameResult* base;
};
// Internal version of API class v8::debug_helper::StackFrameResult.
class StackFrameResult {
public:
StackFrameResult(std::vector<std::unique_ptr<ObjectProperty>> properties) {
properties_ = std::move(properties);
}
d::StackFrameResult* GetPublicView() {
public_view_.num_properties = properties_.size();
properties_raw_.clear();
for (const auto& property : properties_) {
properties_raw_.push_back(property->GetPublicView());
}
public_view_.properties = properties_raw_.data();
public_view_.base = this;
return &public_view_;
}
private:
std::vector<std::unique_ptr<ObjectProperty>> properties_;
StackFrameResultExtended public_view_;
std::vector<d::ObjectProperty*> properties_raw_;
};
class TqObjectVisitor;
// Base class representing a V8 object in the debuggee's address space.
......
......@@ -144,10 +144,15 @@ struct ObjectPropertiesResult {
const char** guessed_types;
};
struct StackFrameResult {
size_t num_properties;
ObjectProperty** properties;
};
// Copies byte_count bytes of memory from the given address in the debuggee to
// the destination buffer.
typedef MemoryAccessResult (*MemoryAccessor)(uintptr_t address,
uint8_t* destination,
void* destination,
size_t byte_count);
// Additional data that can help GetObjectProperties to be more accurate. Any
......@@ -193,6 +198,11 @@ _v8_debug_helper_GetObjectProperties(
const char* type_hint);
V8_DEBUG_HELPER_EXPORT void _v8_debug_helper_Free_ObjectPropertiesResult(
v8::debug_helper::ObjectPropertiesResult* result);
V8_DEBUG_HELPER_EXPORT v8::debug_helper::StackFrameResult*
_v8_debug_helper_GetStackFrame(
uintptr_t frame_pointer, v8::debug_helper::MemoryAccessor memory_accessor);
V8_DEBUG_HELPER_EXPORT void _v8_debug_helper_Free_StackFrameResult(
v8::debug_helper::StackFrameResult* result);
V8_DEBUG_HELPER_EXPORT const v8::debug_helper::ClassList*
_v8_debug_helper_ListObjectClasses();
V8_DEBUG_HELPER_EXPORT const char* _v8_debug_helper_BitsetName(
......@@ -237,6 +247,20 @@ inline const char* BitsetName(uint64_t payload) {
return _v8_debug_helper_BitsetName(payload);
}
struct DebugHelperStackFrameResultDeleter {
void operator()(v8::debug_helper::StackFrameResult* ptr) {
_v8_debug_helper_Free_StackFrameResult(ptr);
}
};
using StackFrameResultPtr =
std::unique_ptr<StackFrameResult, DebugHelperStackFrameResultDeleter>;
inline StackFrameResultPtr GetStackFrame(
uintptr_t frame_pointer, v8::debug_helper::MemoryAccessor memory_accessor) {
return StackFrameResultPtr(
_v8_debug_helper_GetStackFrame(frame_pointer, memory_accessor));
}
} // namespace debug_helper
} // namespace v8
......
......@@ -8,6 +8,8 @@
#include "heap-constants.h"
#include "include/v8-internal.h"
#include "src/common/external-pointer.h"
#include "src/execution/frame-constants.h"
#include "src/execution/frames.h"
#include "src/execution/isolate-utils.h"
#include "src/objects/string-inl.h"
#include "src/strings/unicode-inl.h"
......@@ -630,6 +632,32 @@ std::unique_ptr<ObjectPropertiesResult> GetObjectProperties(
stream.str(), kSmi);
}
std::unique_ptr<StackFrameResult> GetStackFrame(
uintptr_t frame_pointer, d::MemoryAccessor memory_accessor) {
// Read the data at frame_pointer + kContextOrFrameTypeOffset.
intptr_t context_or_frame_type = 0;
d::MemoryAccessResult validity = memory_accessor(
frame_pointer + CommonFrameConstants::kContextOrFrameTypeOffset,
reinterpret_cast<void*>(&context_or_frame_type), sizeof(intptr_t));
auto props = std::vector<std::unique_ptr<ObjectProperty>>();
if (validity == d::MemoryAccessResult::kOk) {
// If it is context, not frame marker then add new property
// "currently_executing_function".
if (!StackFrame::IsTypeMarker(context_or_frame_type)) {
props.push_back(std::make_unique<ObjectProperty>(
"currently_executing_jsfunction",
CheckTypeName<v8::internal::JSFunction>("v8::internal::JSFunction"),
CheckTypeName<v8::internal::JSFunction*>("v8::internal::JSFunction"),
frame_pointer + StandardFrameConstants::kFunctionOffset, 1,
sizeof(v8::internal::JSFunction),
std::vector<std::unique_ptr<StructProperty>>(),
d::PropertyKind::kSingle));
}
}
return std::make_unique<StackFrameResult>(std::move(props));
}
} // namespace debug_helper_internal
} // namespace internal
} // namespace v8
......@@ -652,4 +680,16 @@ V8_DEBUG_HELPER_EXPORT void _v8_debug_helper_Free_ObjectPropertiesResult(
std::unique_ptr<di::ObjectPropertiesResult> ptr(
static_cast<di::ObjectPropertiesResultExtended*>(result)->base);
}
V8_DEBUG_HELPER_EXPORT d::StackFrameResult* _v8_debug_helper_GetStackFrame(
uintptr_t frame_pointer, d::MemoryAccessor memory_accessor) {
return di::GetStackFrame(frame_pointer, memory_accessor)
.release()
->GetPublicView();
}
V8_DEBUG_HELPER_EXPORT void _v8_debug_helper_Free_StackFrameResult(
d::StackFrameResult* result) {
std::unique_ptr<di::StackFrameResult> ptr(
static_cast<di::StackFrameResultExtended*>(result)->base);
}
}
......@@ -4,7 +4,11 @@
#include "tools/v8windbg/src/local-variables.h"
#include <vector>
#include "tools/v8windbg/base/utilities.h"
#include "tools/v8windbg/src/object-inspection.h"
#include "tools/v8windbg/src/v8-debug-helper-interop.h"
#include "tools/v8windbg/src/v8windbg-extension.h"
V8LocalVariables::V8LocalVariables(WRL::ComPtr<IModelPropertyAccessor> original,
......@@ -110,6 +114,14 @@ IFACEMETHODIMP V8LocalVariables::GetValue(PCWSTR key, IModelObject* context,
RETURN_IF_FAIL(
result->SetKey(L"memory interpreted as Objects", array.Get(), nullptr));
std::vector<Property> properties = GetStackFrame(host_context, frame_offset);
for (const auto& prop : properties) {
WRL::ComPtr<IModelObject> property;
RETURN_IF_FAIL(GetModelForProperty(prop, host_context, &property));
result->SetKey(reinterpret_cast<const wchar_t*>(prop.name.c_str()),
property.Get(), nullptr);
}
*value = result.Detach();
return S_OK;
}
......
......@@ -330,31 +330,6 @@ HRESULT GetModelForCustomArray(const Property& prop,
context_data.Get(), result);
}
// Creates an IModelObject representing the data in the given property.
HRESULT GetModelForProperty(const Property& prop,
WRL::ComPtr<IDebugHostContext>& sp_ctx,
IModelObject** result) {
switch (prop.type) {
case PropertyType::kPointer:
return GetModelForBasicField(prop.addr_value, prop.type_name,
prop.uncompressed_type_name, sp_ctx, result);
case PropertyType::kStruct:
return GetModelForStruct(prop.addr_value, prop.fields, sp_ctx, result);
case PropertyType::kArray:
case PropertyType::kStructArray:
if (prop.type == PropertyType::kArray &&
prop.type_name == ConvertToU16String(prop.uncompressed_type_name)) {
// An array of things that are not structs or compressed tagged values
// is most cleanly represented by a native array.
return GetModelForNativeArray(prop.addr_value, prop.type_name,
prop.length, sp_ctx, result);
}
// Otherwise, we must construct a custom iterable object.
return GetModelForCustomArray(prop, sp_ctx, result);
default:
return E_FAIL;
}
}
// Creates an IModelObject representing the data in an array at the given index.
// context_object is expected to be an object of the form created by
......@@ -693,3 +668,29 @@ IFACEMETHODIMP InspectV8ObjectMethod::Call(IModelObject* p_context_object,
return CreateSyntheticObjectForV8Object(sp_ctx.Get(), cached_object.Get(),
pp_result);
}
// Creates an IModelObject representing the data in the given property.
HRESULT GetModelForProperty(const Property& prop,
WRL::ComPtr<IDebugHostContext>& sp_ctx,
IModelObject** result) {
switch (prop.type) {
case PropertyType::kPointer:
return GetModelForBasicField(prop.addr_value, prop.type_name,
prop.uncompressed_type_name, sp_ctx, result);
case PropertyType::kStruct:
return GetModelForStruct(prop.addr_value, prop.fields, sp_ctx, result);
case PropertyType::kArray:
case PropertyType::kStructArray:
if (prop.type == PropertyType::kArray &&
prop.type_name == ConvertToU16String(prop.uncompressed_type_name)) {
// An array of things that are not structs or compressed tagged values
// is most cleanly represented by a native array.
return GetModelForNativeArray(prop.addr_value, prop.type_name,
prop.length, sp_ctx, result);
}
// Otherwise, we must construct a custom iterable object.
return GetModelForCustomArray(prop, sp_ctx, result);
default:
return E_FAIL;
}
}
......@@ -290,4 +290,8 @@ class InspectV8ObjectMethod
IKeyStore** pp_metadata);
};
HRESULT GetModelForProperty(const Property& prop,
WRL::ComPtr<IDebugHostContext>& sp_ctx,
IModelObject** result);
#endif // V8_TOOLS_V8WINDBG_SRC_OBJECT_INSPECTION_H_
......@@ -29,7 +29,7 @@ class MemReaderScope {
private:
MemReaderScope(const MemReaderScope&) = delete;
MemReaderScope& operator=(const MemReaderScope&) = delete;
static d::MemoryAccessResult Read(uintptr_t address, uint8_t* destination,
static d::MemoryAccessResult Read(uintptr_t address, void* destination,
size_t byte_count) {
ULONG64 bytes_read;
Location loc{address};
......@@ -81,26 +81,12 @@ V8HeapObject::V8HeapObject(V8HeapObject&&) = default;
V8HeapObject& V8HeapObject::operator=(const V8HeapObject&) = default;
V8HeapObject& V8HeapObject::operator=(V8HeapObject&&) = default;
V8HeapObject GetHeapObject(WRL::ComPtr<IDebugHostContext> sp_context,
uint64_t tagged_ptr, uint64_t referring_pointer,
const char* type_name, bool is_compressed) {
// Read the value at the address, and see if it is a tagged pointer
V8HeapObject obj;
MemReaderScope reader_scope(sp_context);
d::HeapAddresses heap_addresses = {0, 0, 0, 0};
// TODO ideally we'd provide real heap page pointers. For now, just testing
// decompression based on the pointer to wherever we found this value,
// which is likely (though not guaranteed) to be a heap pointer itself.
heap_addresses.any_heap_pointer = referring_pointer;
auto props = d::GetObjectProperties(tagged_ptr, reader_scope.GetReader(),
heap_addresses, type_name);
obj.friendly_name = ConvertToU16String(props->brief);
for (size_t property_index = 0; property_index < props->num_properties;
std::vector<Property> GetPropertiesAsVector(size_t num_properties,
d::ObjectProperty** properties) {
std::vector<Property> result;
for (size_t property_index = 0; property_index < num_properties;
++property_index) {
const auto& source_prop = *props->properties[property_index];
const auto& source_prop = *(properties)[property_index];
Property dest_prop(ConvertToU16String(source_prop.name),
ConvertToU16String(source_prop.type),
source_prop.decompressed_type, source_prop.address,
......@@ -126,8 +112,30 @@ V8HeapObject GetHeapObject(WRL::ComPtr<IDebugHostContext> sp_context,
struct_field.shift_bits});
}
}
obj.properties.push_back(dest_prop);
result.push_back(dest_prop);
}
return result;
}
V8HeapObject GetHeapObject(WRL::ComPtr<IDebugHostContext> sp_context,
uint64_t tagged_ptr, uint64_t referring_pointer,
const char* type_name, bool is_compressed) {
// Read the value at the address, and see if it is a tagged pointer
V8HeapObject obj;
MemReaderScope reader_scope(sp_context);
d::HeapAddresses heap_addresses = {0, 0, 0, 0};
// TODO ideally we'd provide real heap page pointers. For now, just testing
// decompression based on the pointer to wherever we found this value,
// which is likely (though not guaranteed) to be a heap pointer itself.
heap_addresses.any_heap_pointer = referring_pointer;
auto props = d::GetObjectProperties(tagged_ptr, reader_scope.GetReader(),
heap_addresses, type_name);
obj.friendly_name = ConvertToU16String(props->brief);
obj.properties =
GetPropertiesAsVector(props->num_properties, props->properties);
// For each guessed type, create a synthetic property that will request data
// about the same object again but with a more specific type hint.
......@@ -157,3 +165,12 @@ std::vector<std::u16string> ListObjectClasses() {
}
const char* BitsetName(uint64_t payload) { return d::BitsetName(payload); }
std::vector<Property> GetStackFrame(WRL::ComPtr<IDebugHostContext> sp_context,
uint64_t frame_pointer) {
MemReaderScope reader_scope(sp_context);
auto props = d::GetStackFrame(static_cast<uintptr_t>(frame_pointer),
reader_scope.GetReader());
return GetPropertiesAsVector(props->num_properties, props->properties);
}
......@@ -137,4 +137,7 @@ std::vector<std::u16string> ListObjectClasses();
const char* BitsetName(uint64_t payload);
std::vector<Property> GetStackFrame(WRL::ComPtr<IDebugHostContext> sp_context,
uint64_t frame_pointer);
#endif // V8_TOOLS_V8WINDBG_SRC_V8_DEBUG_HELPER_INTEROP_H_
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment