Upload Kmake

This commit is contained in:
Gorochu
2026-05-26 23:36:42 -07:00
parent ba051b2f74
commit 555ec72358
41615 changed files with 13344630 additions and 1 deletions

119
deps/v8/tools/debug_helper/BUILD.gn vendored Normal file
View File

@ -0,0 +1,119 @@
# Copyright 2019 the V8 project authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("../../gni/snapshot_toolchain.gni")
import("../../gni/v8.gni")
config("internal_config") {
visibility = [ ":*" ] # Only targets in this file can depend on this.
if (is_component_build) {
defines = [ "BUILDING_V8_DEBUG_HELPER" ]
}
configs = [ "../..:v8_tracing_config" ]
include_dirs = [
".",
"../..",
"$target_gen_dir",
"$target_gen_dir/../..",
]
}
# This config should be applied to code using v8_debug_helper.
config("external_config") {
if (is_component_build) {
defines = [ "USING_V8_DEBUG_HELPER" ]
}
configs = [ "../..:external_config" ]
include_dirs = [ "." ]
}
action("run_mkgrokdump") {
testonly = true
visibility = [ ":*" ]
deps = [
"../..:run_mksnapshot_default",
"../../test/mkgrokdump:mkgrokdump($v8_generator_toolchain)",
]
script = "../run.py"
outputs = [ "$target_gen_dir/v8heapconst.py" ]
args = [
"./" + rebase_path(
get_label_info(
"../../test/mkgrokdump:mkgrokdump($v8_generator_toolchain)",
"root_out_dir") + "/mkgrokdump",
root_build_dir),
"--outfile",
rebase_path("$target_gen_dir/v8heapconst.py", root_build_dir),
]
}
action("gen_heap_constants") {
testonly = true
visibility = [ ":*" ]
deps = [ ":run_mkgrokdump" ]
script = "gen-heap-constants.py"
outputs = [ "$target_gen_dir/heap-constants-gen.cc" ]
args = [
rebase_path(target_gen_dir, root_build_dir),
rebase_path("$target_gen_dir/heap-constants-gen.cc", root_build_dir),
]
}
v8_component("v8_debug_helper_internal") {
testonly = true
public = [ "debug-helper.h" ]
sources = [
"$target_gen_dir/../../torque-generated/class-debug-readers.cc",
"$target_gen_dir/../../torque-generated/class-debug-readers.h",
"$target_gen_dir/../../torque-generated/debug-macros.cc",
"$target_gen_dir/../../torque-generated/debug-macros.h",
"$target_gen_dir/../../torque-generated/instance-types.h",
"$target_gen_dir/heap-constants-gen.cc",
"../../src/common/ptr-compr.cc",
"compiler-types.cc",
"debug-helper-internal.cc",
"debug-helper-internal.h",
"debug-helper.h",
"get-object-properties.cc",
"heap-constants.cc",
"heap-constants.h",
]
deps = [
":gen_heap_constants",
"../..:generate_bytecode_builtins_list",
"../..:run_torque",
"../..:v8_abseil",
"../..:v8_headers",
"../..:v8_internal_headers",
"../..:v8_libbase",
"../..:v8_shared_internal_headers",
"../..:v8_tracing",
]
configs = [ ":internal_config" ]
if (v8_enable_i18n_support) {
configs += [ "$v8_icu_path:icu_config" ]
}
remove_configs = [ "//build/config/compiler:no_rtti" ]
configs += [ "//build/config/compiler:rtti" ]
}
group("v8_debug_helper") {
testonly = true
public_deps = [ ":v8_debug_helper_internal" ]
public_configs = [ ":external_config" ]
}

3
deps/v8/tools/debug_helper/DEPS vendored Normal file
View File

@ -0,0 +1,3 @@
include_rules = [
"+torque-generated"
]

1
deps/v8/tools/debug_helper/OWNERS vendored Normal file
View File

@ -0,0 +1 @@
seth.brenith@microsoft.com

6
deps/v8/tools/debug_helper/README.md vendored Normal file
View File

@ -0,0 +1,6 @@
# V8 debug helper
This library is for debugging V8 itself, not debugging JavaScript running within
V8. It is designed to be called from a debugger extension running within a
native debugger such as WinDbg or LLDB. It can be used on live processes or
crash dumps, and cannot assume that all memory is available in a dump.

View File

@ -0,0 +1,31 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "debug-helper-internal.h"
#include "src/compiler/turbofan-types.h"
namespace ic = v8::internal::compiler;
extern "C" {
V8_DEBUG_HELPER_EXPORT const char* _v8_debug_helper_BitsetName(
uint64_t payload) {
// Check if payload is a bitset and return the bitset type.
// This line is duplicating the logic from Type::IsBitset.
bool is_bit_set = payload & 1;
if (!is_bit_set) return nullptr;
ic::BitsetType::bitset bits =
static_cast<ic::BitsetType::bitset>(payload ^ 1u);
switch (bits) {
#define RETURN_NAMED_TYPE(type, value) \
case ic::BitsetType::k##type: \
return #type;
PROPER_BITSET_TYPE_LIST(RETURN_NAMED_TYPE)
INTERNAL_BITSET_TYPE_LIST(RETURN_NAMED_TYPE)
#undef RETURN_NAMED_TYPE
default:
return nullptr;
}
}
}

View File

@ -0,0 +1,74 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "debug-helper-internal.h"
#include "src/common/ptr-compr-inl.h"
#include "torque-generated/class-debug-readers.h"
namespace i = v8::internal;
namespace v8 {
namespace internal {
namespace debug_helper_internal {
bool IsPointerCompressed(uintptr_t address) {
#if COMPRESS_POINTERS_BOOL
return address < i::kPtrComprCageReservationSize;
#else
return false;
#endif
}
uintptr_t EnsureDecompressed(uintptr_t address,
uintptr_t any_uncompressed_ptr) {
if (!COMPRESS_POINTERS_BOOL || !IsPointerCompressed(address)) return address;
#ifdef V8_COMPRESS_POINTERS
Address base =
V8HeapCompressionScheme::GetPtrComprCageBaseAddress(any_uncompressed_ptr);
if (base != V8HeapCompressionScheme::base()) {
V8HeapCompressionScheme::InitBase(base);
}
#endif // V8_COMPRESS_POINTERS
// TODO(v8:11880): ExternalCodeCompressionScheme might be needed here for
// decompressing Code pointers from external code space.
return i::V8HeapCompressionScheme::DecompressTagged(
any_uncompressed_ptr, static_cast<i::Tagged_t>(address));
}
d::PropertyKind GetArrayKind(d::MemoryAccessResult mem_result) {
d::PropertyKind indexed_field_kind{};
switch (mem_result) {
case d::MemoryAccessResult::kOk:
indexed_field_kind = d::PropertyKind::kArrayOfKnownSize;
break;
case d::MemoryAccessResult::kAddressNotValid:
indexed_field_kind =
d::PropertyKind::kArrayOfUnknownSizeDueToInvalidMemory;
break;
default:
indexed_field_kind =
d::PropertyKind::kArrayOfUnknownSizeDueToValidButInaccessibleMemory;
break;
}
return indexed_field_kind;
}
std::vector<std::unique_ptr<ObjectProperty>> TqObject::GetProperties(
d::MemoryAccessor accessor) const {
return std::vector<std::unique_ptr<ObjectProperty>>();
}
const char* TqObject::GetName() const { return "v8::internal::Object"; }
void TqObject::Visit(TqObjectVisitor* visitor) const {
visitor->VisitObject(this);
}
bool TqObject::IsSuperclassOf(const TqObject* other) const {
return GetName() != other->GetName();
}
} // namespace debug_helper_internal
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,244 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_TOOLS_DEBUG_HELPER_DEBUG_HELPER_INTERNAL_H_
#define V8_TOOLS_DEBUG_HELPER_DEBUG_HELPER_INTERNAL_H_
// This file defines internal versions of the public API structs. These should
// all be tidy and simple classes which maintain proper ownership (unique_ptr)
// of each other. Each contains an instance of its corresponding public type,
// which can be filled out with GetPublicView.
#include <memory>
#include <string>
#include <vector>
#include "debug-helper.h"
#include "src/common/globals.h"
#include "src/objects/instance-type.h"
namespace d = v8::debug_helper;
namespace v8 {
namespace internal {
namespace debug_helper_internal {
// A value that was read from the debuggee's memory.
template <typename TValue>
struct Value {
d::MemoryAccessResult validity;
TValue value;
};
// Internal version of API class v8::debug_helper::PropertyBase.
class PropertyBase {
public:
PropertyBase(std::string name, std::string type) : name_(name), type_(type) {}
void SetFieldsOnPublicView(d::PropertyBase* public_view) {
public_view->name = name_.c_str();
public_view->type = type_.c_str();
}
private:
std::string name_;
std::string type_;
};
// Internal version of API class v8::debug_helper::StructProperty.
class StructProperty : public PropertyBase {
public:
StructProperty(std::string name, std::string type, size_t offset,
uint8_t num_bits, uint8_t shift_bits)
: PropertyBase(std::move(name), std::move(type)),
offset_(offset),
num_bits_(num_bits),
shift_bits_(shift_bits) {}
d::StructProperty* GetPublicView() {
PropertyBase::SetFieldsOnPublicView(&public_view_);
public_view_.offset = offset_;
public_view_.num_bits = num_bits_;
public_view_.shift_bits = shift_bits_;
return &public_view_;
}
private:
size_t offset_;
uint8_t num_bits_;
uint8_t shift_bits_;
d::StructProperty public_view_;
};
// Internal version of API class v8::debug_helper::ObjectProperty.
class ObjectProperty : public PropertyBase {
public:
ObjectProperty(std::string name, std::string type, uintptr_t address,
size_t num_values, size_t size,
std::vector<std::unique_ptr<StructProperty>> struct_fields,
d::PropertyKind kind)
: PropertyBase(std::move(name), std::move(type)),
address_(address),
num_values_(num_values),
size_(size),
struct_fields_(std::move(struct_fields)),
kind_(kind) {}
d::ObjectProperty* GetPublicView() {
PropertyBase::SetFieldsOnPublicView(&public_view_);
public_view_.address = address_;
public_view_.num_values = num_values_;
public_view_.size = size_;
public_view_.num_struct_fields = struct_fields_.size();
struct_fields_raw_.clear();
for (const auto& property : struct_fields_) {
struct_fields_raw_.push_back(property->GetPublicView());
}
public_view_.struct_fields = struct_fields_raw_.data();
public_view_.kind = kind_;
return &public_view_;
}
private:
uintptr_t address_;
size_t num_values_;
size_t size_;
std::vector<std::unique_ptr<StructProperty>> struct_fields_;
d::PropertyKind kind_;
d::ObjectProperty public_view_;
std::vector<d::StructProperty*> struct_fields_raw_;
};
class ObjectPropertiesResult;
struct ObjectPropertiesResultExtended : public d::ObjectPropertiesResult {
// Back reference for cleanup.
debug_helper_internal::ObjectPropertiesResult* base;
};
// Internal version of API class v8::debug_helper::ObjectPropertiesResult.
class ObjectPropertiesResult {
public:
ObjectPropertiesResult(d::TypeCheckResult type_check_result,
std::string brief, std::string type)
: type_check_result_(type_check_result), brief_(brief), type_(type) {}
ObjectPropertiesResult(
d::TypeCheckResult type_check_result, std::string brief, std::string type,
std::vector<std::unique_ptr<ObjectProperty>> properties,
std::vector<std::string> guessed_types)
: ObjectPropertiesResult(type_check_result, brief, type) {
properties_ = std::move(properties);
guessed_types_ = std::move(guessed_types);
}
void Prepend(const char* prefix) { brief_ = prefix + brief_; }
d::ObjectPropertiesResult* GetPublicView() {
public_view_.type_check_result = type_check_result_;
public_view_.brief = brief_.c_str();
public_view_.type = type_.c_str();
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_.num_guessed_types = guessed_types_.size();
guessed_types_raw_.clear();
for (const auto& guess : guessed_types_) {
guessed_types_raw_.push_back(guess.c_str());
}
public_view_.guessed_types = guessed_types_raw_.data();
public_view_.base = this;
return &public_view_;
}
private:
d::TypeCheckResult type_check_result_;
std::string brief_;
std::string type_;
std::vector<std::unique_ptr<ObjectProperty>> properties_;
std::vector<std::string> guessed_types_;
ObjectPropertiesResultExtended public_view_;
std::vector<d::ObjectProperty*> properties_raw_;
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.
// Subclasses for specific object types are generated by the Torque compiler.
class TqObject {
public:
TqObject(uintptr_t address) : address_(address) {}
virtual ~TqObject() = default;
virtual std::vector<std::unique_ptr<ObjectProperty>> GetProperties(
d::MemoryAccessor accessor) const;
virtual const char* GetName() const;
virtual void Visit(TqObjectVisitor* visitor) const;
virtual bool IsSuperclassOf(const TqObject* other) const;
protected:
uintptr_t address_;
};
// A helpful template so that generated code can be sure that a string type name
// actually resolves to a type, by repeating the name as the template parameter
// and the value.
template <typename T>
const char* CheckTypeName(const char* name) {
return name;
}
// In ptr-compr builds, returns whether the address looks like a compressed
// pointer (zero-extended from 32 bits). Otherwise returns false because no
// pointers can be compressed.
bool IsPointerCompressed(uintptr_t address);
// If the given address looks like a compressed pointer, returns a decompressed
// representation of it. Otherwise returns the address unmodified.
uintptr_t EnsureDecompressed(uintptr_t address,
uintptr_t any_uncompressed_address);
// Converts the MemoryAccessResult from attempting to read an array's length
// into the corresponding PropertyKind for the array.
d::PropertyKind GetArrayKind(d::MemoryAccessResult mem_result);
} // namespace debug_helper_internal
} // namespace internal
} // namespace v8
#endif

View File

@ -0,0 +1,249 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_TOOLS_DEBUG_HELPER_DEBUG_HELPER_H_
#define V8_TOOLS_DEBUG_HELPER_DEBUG_HELPER_H_
// This file defines the public interface to v8_debug_helper.
#include <cstdint>
#include <memory>
#if defined(_WIN32)
#ifdef BUILDING_V8_DEBUG_HELPER
#define V8_DEBUG_HELPER_EXPORT __declspec(dllexport)
#elif USING_V8_DEBUG_HELPER
#define V8_DEBUG_HELPER_EXPORT __declspec(dllimport)
#else
#define V8_DEBUG_HELPER_EXPORT
#endif
#else // defined(_WIN32)
#if defined(BUILDING_V8_DEBUG_HELPER) || USING_V8_DEBUG_HELPER
#define V8_DEBUG_HELPER_EXPORT __attribute__((visibility("default")))
#else
#define V8_DEBUG_HELPER_EXPORT
#endif
#endif // defined(_WIN32)
namespace v8 {
namespace debug_helper {
// Possible results when attempting to fetch memory from the debuggee.
enum class MemoryAccessResult {
kOk,
kAddressNotValid,
kAddressValidButInaccessible, // Possible in incomplete dump.
};
// Information about how this tool discovered the type of the object.
enum class TypeCheckResult {
// Success cases:
kSmi,
kWeakRef,
kUsedMap,
kKnownMapPointer,
kUsedTypeHint,
// Failure cases:
kUnableToDecompress, // Caller must provide the heap range somehow.
kObjectPointerInvalid,
kObjectPointerValidButInaccessible, // Possible in incomplete dump.
kMapPointerInvalid,
kMapPointerValidButInaccessible, // Possible in incomplete dump.
kUnknownInstanceType,
kUnknownTypeHint,
};
enum class PropertyKind {
kSingle,
kArrayOfKnownSize,
kArrayOfUnknownSizeDueToInvalidMemory,
kArrayOfUnknownSizeDueToValidButInaccessibleMemory,
};
struct PropertyBase {
const char* name;
// Statically-determined type, such as from .tq definition. Can be an empty
// string if this property is itself a Torque-defined struct; in that case use
// |struct_fields| instead. This type should be treated as if it were used in
// the v8::internal namespace; that is, type "X::Y" can mean any of the
// following, in order of decreasing preference:
// - v8::internal::X::Y
// - v8::X::Y
// - X::Y
const char* type;
};
struct StructProperty : public PropertyBase {
// The offset from the beginning of the struct to this field.
size_t offset;
// The number of bits that are present, if this value is a bitfield. Zero
// indicates that this value is not a bitfield (the full value is stored).
uint8_t num_bits;
// The number of bits by which this value has been left-shifted for storage as
// a bitfield.
uint8_t shift_bits;
};
struct ObjectProperty : public PropertyBase {
// The address where the property value can be found in the debuggee's address
// space, or the address of the first value for an array.
uintptr_t address;
// If kind indicates an array of unknown size, num_values will be 0 and debug
// tools should display this property as a raw pointer. Note that there is a
// semantic difference between num_values=1 and kind=kSingle (normal property)
// versus num_values=1 and kind=kArrayOfKnownSize (one-element array).
size_t num_values;
// The number of bytes occupied by a single instance of the value type for
// this property. This can also be used as the array stride because arrays are
// tightly packed like in C.
size_t size;
// If the property is a struct made up of several pieces of data packed
// together, then the |struct_fields| array contains descriptions of those
// fields.
size_t num_struct_fields;
StructProperty** struct_fields;
PropertyKind kind;
};
struct ObjectPropertiesResult {
TypeCheckResult type_check_result;
const char* brief;
const char* type; // Runtime type of the object.
size_t num_properties;
ObjectProperty** properties;
// If not all relevant memory is available, GetObjectProperties may respond
// with a technically correct but uninteresting type such as HeapObject, and
// use other heuristics to make reasonable guesses about what specific type
// the object actually is. You may request data about the same object again
// using any of these guesses as the type hint, but the results should be
// formatted to the user in a way that clearly indicates that they're only
// guesses.
size_t num_guessed_types;
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,
void* destination,
size_t byte_count);
// Additional data that can help GetObjectProperties to be more accurate. Any
// fields you don't know can be set to zero and this library will do the best it
// can with the information available.
struct HeapAddresses {
// Beginning of allocated space for various kinds of data. These can help us
// to detect certain common objects that are placed in memory during startup.
// These values might be provided via name-value pairs in CrashPad dumps.
// Otherwise, they can be obtained as follows:
// 1. Get the Isolate pointer for the current thread. It might be somewhere on
// the stack, or it might be accessible from thread-local storage with the
// key stored in v8::internal::Isolate::isolate_key_.
// 2. Get isolate->heap_.map_space_->memory_chunk_list_.front_ and similar for
// old_space_ and read_only_space_.
uintptr_t map_space_first_page;
uintptr_t old_space_first_page;
uintptr_t read_only_space_first_page;
// Any valid heap pointer address. On platforms where pointer compression is
// enabled, this can allow us to get data from compressed pointers even if the
// other data above is not provided.
uintptr_t any_heap_pointer;
// A pointer to the static array
// v8::internal::MemoryChunk::metadata_pointer_table_.
uintptr_t metadata_pointer_table;
};
} // namespace debug_helper
} // namespace v8
extern "C" {
// Raw library interface. If possible, use functions in v8::debug_helper
// namespace instead because they use smart pointers to prevent leaks.
V8_DEBUG_HELPER_EXPORT v8::debug_helper::ObjectPropertiesResult*
_v8_debug_helper_GetObjectProperties(
uintptr_t object, v8::debug_helper::MemoryAccessor memory_accessor,
const v8::debug_helper::HeapAddresses& heap_addresses,
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 char* _v8_debug_helper_BitsetName(
uint64_t payload);
}
namespace v8 {
namespace debug_helper {
struct DebugHelperObjectPropertiesResultDeleter {
void operator()(v8::debug_helper::ObjectPropertiesResult* ptr) {
_v8_debug_helper_Free_ObjectPropertiesResult(ptr);
}
};
using ObjectPropertiesResultPtr =
std::unique_ptr<ObjectPropertiesResult,
DebugHelperObjectPropertiesResultDeleter>;
// Get information about the given object pointer, which could be:
// - A tagged pointer, strong or weak
// - A cleared weak pointer
// - A compressed tagged pointer, zero-extended to 64 bits
// - A tagged small integer
// The type hint is only used if the object's Map is missing or corrupt. It
// should be the fully-qualified name of a class that inherits from
// v8::internal::Object.
inline ObjectPropertiesResultPtr GetObjectProperties(
uintptr_t object, v8::debug_helper::MemoryAccessor memory_accessor,
const HeapAddresses& heap_addresses, const char* type_hint = nullptr) {
return ObjectPropertiesResultPtr(_v8_debug_helper_GetObjectProperties(
object, memory_accessor, heap_addresses, type_hint));
}
// Return a bitset name for a v8::internal::compiler::Type with payload or null
// if the payload is not a bitset.
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
#endif

View File

@ -0,0 +1,128 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_TORQUE_DEBUG_MACRO_SHIMS_H_
#define V8_TORQUE_DEBUG_MACRO_SHIMS_H_
// This file contains implementations of a few macros that are defined
// as external in Torque, so that generated debug code can work.
#include "src/numbers/integer-literal.h"
#include "src/objects/smi.h"
#include "tools/debug_helper/debug-helper-internal.h"
// For Object::ReadField<T>.
#define READ_FIELD_OR_FAIL(Type, destination, accessor, object, offset) \
do { \
Type value{}; \
d::MemoryAccessResult validity = \
accessor(object - kHeapObjectTag + offset, \
reinterpret_cast<Type*>(&value), sizeof(value)); \
if (validity != d::MemoryAccessResult::kOk) return {validity, {}}; \
destination = value; \
} while (false)
// For TaggedField<T>::load.
#define READ_TAGGED_FIELD_OR_FAIL(destination, accessor, object, offset) \
do { \
Tagged_t value{}; \
d::MemoryAccessResult validity = \
accessor(object - kHeapObjectTag + offset, \
reinterpret_cast<uint8_t*>(&value), sizeof(value)); \
if (validity != d::MemoryAccessResult::kOk) return {validity, {}}; \
destination = EnsureDecompressed(value, object); \
} while (false)
// Process Value struct.
#define ASSIGN_OR_RETURN(dest, val) \
do { \
if ((val).validity != d::MemoryAccessResult::kOk) \
return {(val).validity, {}}; \
dest = (val).value; \
} while (false)
namespace v8 {
namespace internal {
namespace debug_helper_internal {
namespace TorqueDebugMacroShims {
namespace CodeStubAssembler {
inline Value<bool> BoolConstant(d::MemoryAccessor accessor, bool b) {
return {d::MemoryAccessResult::kOk, b};
}
inline Value<intptr_t> ChangeInt32ToIntPtr(d::MemoryAccessor accessor,
int32_t i) {
return {d::MemoryAccessResult::kOk, i};
}
inline Value<uintptr_t> ChangeUint32ToWord(d::MemoryAccessor accessor,
uint32_t u) {
return {d::MemoryAccessResult::kOk, u};
}
inline Value<intptr_t> IntPtrAdd(d::MemoryAccessor accessor, intptr_t a,
intptr_t b) {
return {d::MemoryAccessResult::kOk, a + b};
}
inline Value<intptr_t> IntPtrMul(d::MemoryAccessor accessor, intptr_t a,
intptr_t b) {
return {d::MemoryAccessResult::kOk, a * b};
}
inline Value<bool> IntPtrLessThan(d::MemoryAccessor accessor, intptr_t a,
intptr_t b) {
return {d::MemoryAccessResult::kOk, a < b};
}
inline Value<bool> IntPtrLessThanOrEqual(d::MemoryAccessor accessor, intptr_t a,
intptr_t b) {
return {d::MemoryAccessResult::kOk, a <= b};
}
inline Value<intptr_t> Signed(d::MemoryAccessor accessor, uintptr_t u) {
return {d::MemoryAccessResult::kOk, static_cast<intptr_t>(u)};
}
inline Value<int32_t> SmiUntag(d::MemoryAccessor accessor, uintptr_t s_t) {
Tagged<Smi> s(s_t);
return {d::MemoryAccessResult::kOk, s.value()};
}
inline Value<uintptr_t> SmiFromInt32(d::MemoryAccessor accessor, int32_t i) {
return {d::MemoryAccessResult::kOk, Smi::FromInt(i).ptr()};
}
inline Value<bool> UintPtrLessThan(d::MemoryAccessor accessor, uintptr_t a,
uintptr_t b) {
return {d::MemoryAccessResult::kOk, a < b};
}
inline Value<uint32_t> Unsigned(d::MemoryAccessor accessor, int32_t s) {
return {d::MemoryAccessResult::kOk, static_cast<uint32_t>(s)};
}
#if V8_HOST_ARCH_64_BIT
inline Value<uintptr_t> Unsigned(d::MemoryAccessor accessor, intptr_t s) {
return {d::MemoryAccessResult::kOk, static_cast<uintptr_t>(s)};
}
#endif
inline Value<bool> Word32Equal(d::MemoryAccessor accessor, uint32_t a,
uint32_t b) {
return {d::MemoryAccessResult::kOk, a == b};
}
inline Value<bool> Word32NotEqual(d::MemoryAccessor accessor, uint32_t a,
uint32_t b) {
return {d::MemoryAccessResult::kOk, a != b};
}
// This is used in a nested call where we cannot pass Value<int32_t>.
inline int31_t ConstexprIntegerLiteralToInt31(d::MemoryAccessor accessor,
const IntegerLiteral& i) {
return i.To<int32_t>();
}
inline int32_t ConstexprIntegerLiteralToInt32(d::MemoryAccessor accessor,
const IntegerLiteral& i) {
return i.To<int32_t>();
}
inline intptr_t ConstexprIntegerLiteralToIntptr(d::MemoryAccessor accessor,
const IntegerLiteral& i) {
return i.To<intptr_t>();
}
} // namespace CodeStubAssembler
} // namespace TorqueDebugMacroShims
} // namespace debug_helper_internal
} // namespace internal
} // namespace v8
#endif // V8_TORQUE_DEBUG_MACRO_SHIMS_H_

View File

@ -0,0 +1,89 @@
#!/usr/bin/env python3
# Copyright 2019 the V8 project authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""This program writes a C++ file that can be used to look up whether a given
address matches known object locations. The first argument is the directory
containing the file v8heapconst.py; the second argument is the output .cc file.
"""
import sys
sys.path.insert(0, sys.argv[1])
import v8heapconst
out = """
#include <cstdint>
#include <string>
#include "src/common/ptr-compr-inl.h"
#include "tools/debug_helper/debug-helper-internal.h"
namespace v8 {
namespace internal {
namespace debug_helper_internal {
"""
def iterate_objects(target_space, camel_space_name):
global out
result = []
for (space, offset), (instance_type, name) in v8heapconst.KNOWN_MAPS.items():
if space == target_space:
result.append((offset, name))
for (space, offset), name in v8heapconst.KNOWN_OBJECTS.items():
if space == target_space and (space, offset) not in v8heapconst.KNOWN_MAPS:
result.append((offset, name))
out = out + '\nstd::string FindKnownObjectIn' + camel_space_name \
+ '(uintptr_t offset) {\n switch (offset) {\n'
for offset, name in result:
out = out + ' case ' + str(offset) + ': return "' + name + '";\n'
out = out + ' default: return "";\n }\n}\n'
iterate_objects('map_space', 'MapSpace')
iterate_objects('old_space', 'OldSpace')
iterate_objects('read_only_space', 'ReadOnlySpace')
def iterate_maps(target_space, camel_space_name):
global out
out = out + '\nint FindKnownMapInstanceTypeIn' + camel_space_name \
+ '(uintptr_t offset) {\n switch (offset) {\n'
for (space, offset), (instance_type, name) in v8heapconst.KNOWN_MAPS.items():
if space == target_space:
out = out + ' case ' + str(offset) + ': return ' + str(instance_type) \
+ ';\n'
out = out + ' default: return -1;\n }\n}\n'
iterate_maps('map_space', 'MapSpace')
iterate_maps('old_space', 'OldSpace')
iterate_maps('read_only_space', 'ReadOnlySpace')
out = out + '\nvoid FillInUnknownHeapAddresses(' + \
'd::HeapAddresses* heap_addresses, uintptr_t any_uncompressed_ptr) {\n'
if (hasattr(v8heapconst, 'HEAP_FIRST_PAGES')): # Only exists in ptr-compr builds.
out = out + ' if (heap_addresses->any_heap_pointer == 0) {\n'
out = out + ' heap_addresses->any_heap_pointer = any_uncompressed_ptr;\n'
out = out + ' }\n'
# If we ever try to apply this to CodeSpace we might need to use
# ExternalCodeCompressionScheme instead of V8HeapCompressionScheme for
# decompressing external code pointers below.
expected_spaces = set(['map_space', 'read_only_space', 'old_space'])
for offset, space_name in v8heapconst.HEAP_FIRST_PAGES.items():
if (space_name in expected_spaces):
out = out + ' if (heap_addresses->' + space_name + '_first_page == 0) {\n'
out = out + ' heap_addresses->' + space_name + \
'_first_page = i::V8HeapCompressionScheme::DecompressTagged(' + \
'any_uncompressed_ptr, ' + str(offset) + ');\n'
out = out + ' }\n'
out = out + '}\n'
out = out + '\n}\n}\n}\n'
try:
with open(sys.argv[2], "r") as out_file:
if out == out_file.read():
sys.exit(0) # No modification needed.
except:
pass # File probably doesn't exist; write it.
with open(sys.argv[2], "w") as out_file:
out_file.write(out)

View File

@ -0,0 +1,847 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <optional>
#include <sstream>
#include "debug-helper-internal.h"
#include "heap-constants.h"
#include "include/v8-internal.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/sandbox/external-pointer.h"
#include "src/strings/unicode-inl.h"
#include "torque-generated/class-debug-readers.h"
#include "torque-generated/debug-macros.h"
namespace i = v8::internal;
namespace v8::internal::debug_helper_internal {
constexpr char kTaggedValue[] = "v8::internal::TaggedValue";
constexpr char kSmi[] = "v8::internal::Smi";
constexpr char kHeapObject[] = "v8::internal::HeapObject";
constexpr char kObjectAsStoredInHeap[] =
"v8::internal::TaggedMember<v8::internal::Object>";
std::string AppendAddressAndType(const std::string& brief, uintptr_t address,
const char* type) {
std::stringstream brief_stream;
brief_stream << "0x" << std::hex << address << " <" << type << ">";
return brief.empty() ? brief_stream.str()
: brief + " (" + brief_stream.str() + ")";
}
std::string JoinWithSpace(const std::string& a, const std::string& b) {
return a.empty() || b.empty() ? a + b : a + " " + b;
}
struct TypedObject {
TypedObject(d::TypeCheckResult type_check_result,
std::unique_ptr<TqObject> object)
: type_check_result(type_check_result), object(std::move(object)) {}
// How we discovered the object's type, or why we failed to do so.
d::TypeCheckResult type_check_result;
// Pointer to some TqObject subclass, representing the most specific known
// type for the object.
std::unique_ptr<TqObject> object;
// Collection of other guesses at more specific types than the one represented
// by |object|.
std::vector<TypedObject> possible_types;
};
TypedObject GetTypedObjectByHint(uintptr_t address,
std::string type_hint_string) {
#define TYPE_NAME_CASE(ClassName, ...) \
if (type_hint_string == "v8::internal::" #ClassName) { \
return {d::TypeCheckResult::kUsedTypeHint, \
std::make_unique<Tq##ClassName>(address)}; \
}
TORQUE_INSTANCE_CHECKERS_SINGLE_FULLY_DEFINED(TYPE_NAME_CASE)
TORQUE_INSTANCE_CHECKERS_RANGE_FULLY_DEFINED(TYPE_NAME_CASE)
STRING_CLASS_TYPES(TYPE_NAME_CASE)
#undef TYPE_NAME_CASE
return {d::TypeCheckResult::kUnknownTypeHint,
std::make_unique<TqHeapObject>(address)};
}
TypedObject GetTypedObjectForString(uintptr_t address, i::InstanceType type,
d::TypeCheckResult type_source) {
class StringGetDispatcher : public i::AllStatic {
public:
#define DEFINE_METHOD(ClassName) \
static inline TypedObject Handle##ClassName( \
uintptr_t address, d::TypeCheckResult type_source) { \
return {type_source, std::make_unique<Tq##ClassName>(address)}; \
}
STRING_CLASS_TYPES(DEFINE_METHOD)
#undef DEFINE_METHOD
static inline TypedObject HandleInvalidString(
uintptr_t address, d::TypeCheckResult type_source) {
return {d::TypeCheckResult::kUnknownInstanceType,
std::make_unique<TqString>(address)};
}
};
return String::DispatchToSpecificTypeWithoutCast<StringGetDispatcher>(
type, address, type_source);
}
TypedObject GetTypedObjectByInstanceType(uintptr_t address,
i::InstanceType type,
d::TypeCheckResult type_source) {
switch (type) {
#define INSTANCE_TYPE_CASE(ClassName, INSTANCE_TYPE) \
case i::INSTANCE_TYPE: \
return {type_source, std::make_unique<Tq##ClassName>(address)};
TORQUE_INSTANCE_CHECKERS_SINGLE_FULLY_DEFINED(INSTANCE_TYPE_CASE)
TORQUE_INSTANCE_CHECKERS_MULTIPLE_FULLY_DEFINED(INSTANCE_TYPE_CASE)
#undef INSTANCE_TYPE_CASE
default:
// Special case: concrete subtypes of String are not included in the
// main instance type list because they use the low bits of the instance
// type enum as flags.
if (type <= i::LAST_STRING_TYPE) {
return GetTypedObjectForString(address, type, type_source);
}
#define INSTANCE_RANGE_CASE(ClassName, FIRST_TYPE, LAST_TYPE) \
if (type >= i::FIRST_TYPE && type <= i::LAST_TYPE) { \
return {type_source, std::make_unique<Tq##ClassName>(address)}; \
}
TORQUE_INSTANCE_CHECKERS_RANGE_FULLY_DEFINED(INSTANCE_RANGE_CASE)
#undef INSTANCE_RANGE_CASE
return {d::TypeCheckResult::kUnknownInstanceType,
std::make_unique<TqHeapObject>(address)};
}
}
bool IsTypedHeapObjectInstanceTypeOf(uintptr_t address,
d::MemoryAccessor accessor,
i::InstanceType instance_type) {
auto heap_object = std::make_unique<TqHeapObject>(address);
Value<uintptr_t> map_ptr = heap_object->GetMapValue(accessor);
if (map_ptr.validity == d::MemoryAccessResult::kOk) {
Value<i::InstanceType> type =
TqMap(map_ptr.value).GetInstanceTypeValue(accessor);
if (type.validity == d::MemoryAccessResult::kOk) {
return instance_type == type.value;
}
}
return false;
}
TypedObject GetTypedHeapObject(uintptr_t address, d::MemoryAccessor accessor,
const char* type_hint,
const d::HeapAddresses& heap_addresses) {
auto heap_object = std::make_unique<TqHeapObject>(address);
Value<uintptr_t> map_ptr = heap_object->GetMapValue(accessor);
if (map_ptr.validity != d::MemoryAccessResult::kOk) {
// If we can't read the Map pointer from the object, then we likely can't
// read anything else, so there's not any point in attempting to use the
// type hint. Just return a failure.
return {map_ptr.validity == d::MemoryAccessResult::kAddressNotValid
? d::TypeCheckResult::kObjectPointerInvalid
: d::TypeCheckResult::kObjectPointerValidButInaccessible,
std::move(heap_object)};
}
Value<i::InstanceType> type =
TqMap(map_ptr.value).GetInstanceTypeValue(accessor);
if (type.validity == d::MemoryAccessResult::kOk) {
return GetTypedObjectByInstanceType(address, type.value,
d::TypeCheckResult::kUsedMap);
}
// We can't read the Map, so check whether it is in the list of known Maps,
// as another way to get its instance type.
KnownInstanceType known_map_type =
FindKnownMapInstanceTypes(map_ptr.value, heap_addresses);
if (known_map_type.confidence == KnownInstanceType::Confidence::kHigh) {
DCHECK_EQ(known_map_type.types.size(), 1);
return GetTypedObjectByInstanceType(address, known_map_type.types[0],
d::TypeCheckResult::kKnownMapPointer);
}
// Create a basic result that says that the object is a HeapObject and we
// couldn't read its Map.
TypedObject result = {
type.validity == d::MemoryAccessResult::kAddressNotValid
? d::TypeCheckResult::kMapPointerInvalid
: d::TypeCheckResult::kMapPointerValidButInaccessible,
std::move(heap_object)};
// If a type hint is available, it may give us something more specific than
// HeapObject. However, a type hint of Object would be even less specific, so
// we'll only use the type hint if it's a subclass of HeapObject.
if (type_hint != nullptr) {
TypedObject hint_result = GetTypedObjectByHint(address, type_hint);
if (result.object->IsSuperclassOf(hint_result.object.get())) {
result = std::move(hint_result);
}
}
// If low-confidence results are available from known Maps, include them only
// if they don't contradict the primary type and would provide some additional
// specificity.
for (const i::InstanceType type_guess : known_map_type.types) {
TypedObject guess_result = GetTypedObjectByInstanceType(
address, type_guess, d::TypeCheckResult::kKnownMapPointer);
if (result.object->IsSuperclassOf(guess_result.object.get())) {
result.possible_types.push_back(std::move(guess_result));
}
}
return result;
}
// An object visitor that accumulates the first few characters of a string.
class ReadStringVisitor : public TqObjectVisitor {
public:
struct Result {
std::optional<std::string> maybe_truncated_string;
std::unique_ptr<ObjectProperty> maybe_raw_characters_property;
};
static Result Visit(d::MemoryAccessor accessor,
const d::HeapAddresses& heap_addresses,
const TqString* object) {
ReadStringVisitor visitor(accessor, heap_addresses);
object->Visit(&visitor);
return {visitor.GetString(), visitor.GetRawCharactersProperty()};
}
// Returns the result as UTF-8 once visiting is complete.
std::optional<std::string> GetString() {
if (failed_) return {};
std::vector<char> result(
string_.size() * unibrow::Utf16::kMaxExtraUtf8BytesForOneUtf16CodeUnit);
unsigned write_index = 0;
int prev_char = unibrow::Utf16::kNoPreviousCharacter;
for (size_t read_index = 0; read_index < string_.size(); ++read_index) {
uint16_t character = string_[read_index];
write_index +=
unibrow::Utf8::Encode(result.data() + write_index, character,
prev_char, /*replace_invalid=*/true);
prev_char = character;
}
return std::string(result.data(), write_index);
}
// Returns a property referring to the address of the flattened character
// array, if possible, once visiting is complete.
std::unique_ptr<ObjectProperty> GetRawCharactersProperty() {
if (failed_ || raw_characters_address_ == 0) return {};
DCHECK(size_per_character_ == 1 || size_per_character_ == 2);
const char* type = size_per_character_ == 1 ? "char" : "char16_t";
return std::make_unique<ObjectProperty>(
"raw_characters", type, raw_characters_address_, num_characters_,
size_per_character_, std::vector<std::unique_ptr<StructProperty>>(),
d::PropertyKind::kArrayOfKnownSize);
}
template <typename T>
Value<T> ReadValue(uintptr_t data_address, int32_t index = 0) {
T value{};
d::MemoryAccessResult validity =
accessor_(data_address + index * sizeof(T),
reinterpret_cast<uint8_t*>(&value), sizeof(value));
return {validity, value};
}
template <typename TChar>
void ReadStringCharacters(const TqString* object, uintptr_t data_address) {
int32_t length = GetOrFinish(object->GetLengthValue(accessor_));
if (string_.size() == 0) {
raw_characters_address_ = data_address + index_ * sizeof(TChar);
size_per_character_ = sizeof(TChar);
num_characters_ = std::min(length, limit_) - index_;
}
for (; index_ < length && index_ < limit_ && !done_; ++index_) {
static_assert(sizeof(TChar) <= sizeof(char16_t));
char16_t c = static_cast<char16_t>(
GetOrFinish(ReadValue<TChar>(data_address, index_)));
if (!done_) AddCharacter(c);
}
}
template <typename TChar, typename TString>
void ReadSeqString(const TString* object) {
ReadStringCharacters<TChar>(object, object->GetCharsAddress());
}
void VisitSeqOneByteString(const TqSeqOneByteString* object) override {
ReadSeqString<char>(object);
}
void VisitSeqTwoByteString(const TqSeqTwoByteString* object) override {
ReadSeqString<char16_t>(object);
}
void VisitConsString(const TqConsString* object) override {
uintptr_t first_address = GetOrFinish(object->GetFirstValue(accessor_));
if (done_) return;
auto first =
GetTypedHeapObject(first_address, accessor_, nullptr, heap_addresses_)
.object;
first->Visit(this);
// Cons strings don't have all of their characters in a contiguous memory
// region, so it would be confusing to show the user a raw pointer to the
// character storage for only part of the cons string.
raw_characters_address_ = 0;
if (done_) return;
int32_t first_length = GetOrFinish(
static_cast<TqString*>(first.get())->GetLengthValue(accessor_));
uintptr_t second = GetOrFinish(object->GetSecondValue(accessor_));
if (done_) return;
IndexModifier modifier(this, -first_length, -first_length);
GetTypedHeapObject(second, accessor_, nullptr, heap_addresses_)
.object->Visit(this);
}
void VisitSlicedString(const TqSlicedString* object) override {
uintptr_t parent = GetOrFinish(object->GetParentValue(accessor_));
int32_t length = GetOrFinish(object->GetLengthValue(accessor_));
int32_t offset = i::PlatformSmiTagging::SmiToInt(
GetOrFinish(object->GetOffsetValue(accessor_)));
if (done_) return;
int32_t limit_adjust = offset + length - limit_;
IndexModifier modifier(this, offset, limit_adjust < 0 ? limit_adjust : 0);
GetTypedHeapObject(parent, accessor_, nullptr, heap_addresses_)
.object->Visit(this);
}
void VisitThinString(const TqThinString* object) override {
uintptr_t actual = GetOrFinish(object->GetActualValue(accessor_));
if (done_) return;
GetTypedHeapObject(actual, accessor_, nullptr, heap_addresses_)
.object->Visit(this);
}
bool IsExternalStringCached(const TqExternalString* object) {
// The safest way to get the instance type is to use known map pointers, in
// case the map data is not available.
Value<uintptr_t> map_ptr = object->GetMapValue(accessor_);
DCHECK_IMPLIES(map_ptr.validity == d::MemoryAccessResult::kOk,
!v8::internal::MapWord::IsPacked(map_ptr.value));
uintptr_t map = GetOrFinish(map_ptr);
if (done_) return false;
auto instance_types = FindKnownMapInstanceTypes(map, heap_addresses_);
// Exactly one of the matched instance types should be a string type,
// because all maps for string types are in the same space (read-only
// space). The "uncached" flag on that instance type tells us whether it's
// safe to read the cached data.
for (const auto& type : instance_types.types) {
if ((type & i::kIsNotStringMask) == i::kStringTag &&
(type & i::kStringRepresentationMask) == i::kExternalStringTag) {
return (type & i::kUncachedExternalStringMask) !=
i::kUncachedExternalStringTag;
}
}
// If for some reason we can't find an external string type here (maybe the
// caller provided an external string type as the type hint, but it doesn't
// actually match the in-memory map pointer), then we can't safely use the
// cached data.
return false;
}
template <typename TChar>
void ReadExternalString(const TqExternalString* object) {
// Uncached external strings require knowledge of the embedder. For now, we
// only read cached external strings.
if (IsExternalStringCached(object)) {
ExternalPointer_t resource_data =
GetOrFinish(object->GetResourceDataValue(accessor_));
#ifdef V8_ENABLE_SANDBOX
Address memory_chunk =
MemoryChunk::FromAddress(object->GetMapAddress())->address();
uint32_t metadata_index = GetOrFinish(ReadValue<uint32_t>(
memory_chunk + MemoryChunk::MetadataIndexOffset()));
Address metadata_address = GetOrFinish(ReadValue<Address>(
heap_addresses_.metadata_pointer_table, metadata_index));
Address heap = GetOrFinish(ReadValue<Address>(
metadata_address + MemoryChunkMetadata::HeapOffset()));
Isolate* isolate = Isolate::FromHeap(reinterpret_cast<Heap*>(heap));
Address external_pointer_table_address_address =
isolate->shared_external_pointer_table_address_address();
Address external_pointer_table_address = GetOrFinish(
ReadValue<Address>(external_pointer_table_address_address));
Address external_pointer_table =
GetOrFinish(ReadValue<Address>(external_pointer_table_address));
int32_t index =
static_cast<int32_t>(resource_data >> kExternalPointerIndexShift);
Address tagged_data =
GetOrFinish(ReadValue<Address>(external_pointer_table, index));
// We don't really need to perform the type check here.
Address data_address = tagged_data & kExternalPointerPayloadMask;
#else
uintptr_t data_address = static_cast<uintptr_t>(resource_data);
#endif // V8_ENABLE_SANDBOX
if (done_) return;
ReadStringCharacters<TChar>(object, data_address);
} else {
// TODO(v8:9376): Come up with some way that a caller with full knowledge
// of a particular embedder could provide a callback function for getting
// uncached string data.
AddEllipsisAndFinish();
}
}
void VisitExternalOneByteString(
const TqExternalOneByteString* object) override {
ReadExternalString<char>(object);
}
void VisitExternalTwoByteString(
const TqExternalTwoByteString* object) override {
ReadExternalString<char16_t>(object);
}
void VisitObject(const TqObject* object) override {
// If we fail to find a specific type for a sub-object within a cons string,
// sliced string, or thin string, we will end up here.
AddEllipsisAndFinish();
}
private:
ReadStringVisitor(d::MemoryAccessor accessor,
const d::HeapAddresses& heap_addresses)
: accessor_(accessor),
heap_addresses_(heap_addresses),
index_(0),
limit_(INT32_MAX),
done_(false),
failed_(false) {}
// Unpacks a value that was fetched from the debuggee. If the value indicates
// that it couldn't successfully fetch memory, then prevents further work.
template <typename T>
T GetOrFinish(Value<T> value) {
if (value.validity != d::MemoryAccessResult::kOk) {
AddEllipsisAndFinish();
}
return value.value;
}
void AddEllipsisAndFinish() {
if (!done_) {
done_ = true;
if (string_.empty()) {
failed_ = true;
} else {
string_ += u"...";
}
}
}
void AddCharacter(char16_t c) {
if (string_.size() >= kMaxCharacters) {
AddEllipsisAndFinish();
} else {
string_.push_back(c);
}
}
// Temporarily adds offsets to both index_ and limit_, to handle ConsString
// and SlicedString.
class IndexModifier {
public:
IndexModifier(ReadStringVisitor* that, int32_t index_adjust,
int32_t limit_adjust)
: that_(that),
index_adjust_(index_adjust),
limit_adjust_(limit_adjust) {
that_->index_ += index_adjust_;
that_->limit_ += limit_adjust_;
}
IndexModifier(const IndexModifier&) = delete;
IndexModifier& operator=(const IndexModifier&) = delete;
~IndexModifier() {
that_->index_ -= index_adjust_;
that_->limit_ -= limit_adjust_;
}
private:
ReadStringVisitor* that_;
int32_t index_adjust_;
int32_t limit_adjust_;
};
static constexpr int kMaxCharacters = 80; // How many characters to print.
std::u16string string_; // Result string.
d::MemoryAccessor accessor_;
const d::HeapAddresses& heap_addresses_;
int32_t index_; // Index of next char to read.
int32_t limit_; // Don't read past this index (set by SlicedString).
bool done_; // Whether to stop further work.
bool failed_; // Whether an error was encountered before any valid data.
// If the string's characters are in a contiguous block of memory (including
// sequential strings, external strings where we could determine the raw data
// location, and thin or sliced strings pointing to either of those), then
// after this visitor has run, the character data's address, size per
// character, and number of characters will be present in the following
// fields.
Address raw_characters_address_ = 0;
int32_t size_per_character_ = 0;
int32_t num_characters_ = 0;
};
// An object visitor that supplies extra information for some types.
class AddInfoVisitor : public TqObjectVisitor {
public:
// Returns a descriptive string and a list of properties for the given object.
// Both may be empty, and are meant as an addition or a replacement for,
// the Torque-generated data about the object.
static std::pair<std::string, std::vector<std::unique_ptr<ObjectProperty>>>
Visit(const TqObject* object, d::MemoryAccessor accessor,
const d::HeapAddresses& heap_addresses) {
AddInfoVisitor visitor(accessor, heap_addresses);
object->Visit(&visitor);
return {std::move(visitor.brief_), std::move(visitor.properties_)};
}
void VisitStringImpl(const TqString* object, bool is_sequential) {
auto visit_result =
ReadStringVisitor::Visit(accessor_, heap_addresses_, object);
auto str = visit_result.maybe_truncated_string;
if (str.has_value()) {
brief_ = "\"" + *str + "\"";
}
// Sequential strings already have a "chars" property based on the Torque
// type definition, so there's no need to duplicate it. Otherwise, it is
// useful to display a pointer to the flattened character data if possible.
if (!is_sequential && visit_result.maybe_raw_characters_property) {
properties_.push_back(
std::move(visit_result.maybe_raw_characters_property));
}
}
void VisitString(const TqString* object) override {
VisitStringImpl(object, /*is_sequential=*/false);
}
void VisitSeqString(const TqSeqString* object) override {
VisitStringImpl(object, /*is_sequential=*/true);
}
void VisitJSObject(const TqJSObject* object) override {
// JSObject and its subclasses can be followed directly by an array of
// property values. The start and end offsets of those values are described
// by a pair of values in its Map.
auto map_ptr = object->GetMapValue(accessor_);
if (map_ptr.validity != d::MemoryAccessResult::kOk) {
return; // Can't read the JSObject. Nothing useful to do.
}
DCHECK(!v8::internal::MapWord::IsPacked(map_ptr.value));
TqMap map(map_ptr.value);
// On JSObject instances, this value is the start of in-object properties.
// The constructor function index option is only for primitives.
auto start_offset =
map.GetInobjectPropertiesStartOrConstructorFunctionIndexValue(
accessor_);
// The total size of the object in memory. This may include over-allocated
// expansion space that doesn't correspond to any user-accessible property.
auto instance_size = map.GetInstanceSizeInWordsValue(accessor_);
if (start_offset.validity != d::MemoryAccessResult::kOk ||
instance_size.validity != d::MemoryAccessResult::kOk) {
return; // Can't read the Map. Nothing useful to do.
}
int num_properties = instance_size.value - start_offset.value;
if (num_properties > 0) {
properties_.push_back(std::make_unique<ObjectProperty>(
"in-object properties", kObjectAsStoredInHeap,
object->GetMapAddress() + start_offset.value * i::kTaggedSize,
num_properties, i::kTaggedSize,
std::vector<std::unique_ptr<StructProperty>>(),
d::PropertyKind::kArrayOfKnownSize));
}
}
private:
AddInfoVisitor(d::MemoryAccessor accessor,
const d::HeapAddresses& heap_addresses)
: accessor_(accessor), heap_addresses_(heap_addresses) {}
// Inputs used by this visitor:
d::MemoryAccessor accessor_;
const d::HeapAddresses& heap_addresses_;
// Outputs generated by this visitor:
// A brief description of the object.
std::string brief_;
// A list of extra properties to append after the automatic ones that are
// created for all Torque-defined class fields.
std::vector<std::unique_ptr<ObjectProperty>> properties_;
};
std::unique_ptr<ObjectPropertiesResult> GetHeapObjectPropertiesNotCompressed(
uintptr_t address, d::MemoryAccessor accessor, const char* type_hint,
const d::HeapAddresses& heap_addresses) {
// Regardless of whether we can read the object itself, maybe we can find its
// pointer in the list of known objects.
std::string brief = FindKnownObject(address, heap_addresses);
TypedObject typed =
GetTypedHeapObject(address, accessor, type_hint, heap_addresses);
auto props = typed.object->GetProperties(accessor);
// Use the AddInfoVisitor to get any extra properties or descriptive text that
// can't be directly derived from Torque class definitions.
auto extra_info =
AddInfoVisitor::Visit(typed.object.get(), accessor, heap_addresses);
brief = JoinWithSpace(brief, extra_info.first);
// Overwrite existing properties if they have the same name.
for (size_t i = 0; i < extra_info.second.size(); i++) {
bool overwrite = false;
for (size_t j = 0; j < props.size(); j++) {
if (strcmp(props[j]->GetPublicView()->name,
extra_info.second[i]->GetPublicView()->name) == 0) {
props[j] = std::move(extra_info.second[i]);
overwrite = true;
break;
}
}
if (overwrite) continue;
props.push_back(std::move(extra_info.second[i]));
}
brief = AppendAddressAndType(brief, address, typed.object->GetName());
// Convert the low-confidence guessed types to a list of strings as expected
// for the response.
std::vector<std::string> guessed_types;
for (const auto& guess : typed.possible_types) {
guessed_types.push_back(guess.object->GetName());
}
return std::make_unique<ObjectPropertiesResult>(
typed.type_check_result, brief, typed.object->GetName(), std::move(props),
std::move(guessed_types));
}
std::unique_ptr<ObjectPropertiesResult> GetHeapObjectPropertiesMaybeCompressed(
uintptr_t address, d::MemoryAccessor memory_accessor,
d::HeapAddresses heap_addresses, const char* type_hint) {
// Try to figure out the heap range, for pointer compression (this is unused
// if pointer compression is disabled).
uintptr_t any_uncompressed_ptr = 0;
if (!IsPointerCompressed(address)) any_uncompressed_ptr = address;
if (any_uncompressed_ptr == 0)
any_uncompressed_ptr = heap_addresses.any_heap_pointer;
if (any_uncompressed_ptr == 0)
any_uncompressed_ptr = heap_addresses.map_space_first_page;
if (any_uncompressed_ptr == 0)
any_uncompressed_ptr = heap_addresses.old_space_first_page;
if (any_uncompressed_ptr == 0)
any_uncompressed_ptr = heap_addresses.read_only_space_first_page;
#ifdef V8_COMPRESS_POINTERS
Address base =
V8HeapCompressionScheme::GetPtrComprCageBaseAddress(any_uncompressed_ptr);
if (base != V8HeapCompressionScheme::base()) {
V8HeapCompressionScheme::InitBase(base);
}
#endif // V8_COMPRESS_POINTERS
FillInUnknownHeapAddresses(&heap_addresses, any_uncompressed_ptr);
if (any_uncompressed_ptr == 0) {
// We can't figure out the heap range. Just check for known objects.
std::string brief = FindKnownObject(address, heap_addresses);
brief = AppendAddressAndType(brief, address, kTaggedValue);
return std::make_unique<ObjectPropertiesResult>(
d::TypeCheckResult::kUnableToDecompress, brief, kTaggedValue);
}
address = EnsureDecompressed(address, any_uncompressed_ptr);
return GetHeapObjectPropertiesNotCompressed(address, memory_accessor,
type_hint, heap_addresses);
}
std::unique_ptr<ObjectPropertiesResult> GetObjectProperties(
uintptr_t address, d::MemoryAccessor memory_accessor,
const d::HeapAddresses& heap_addresses, const char* type_hint) {
if (static_cast<uint32_t>(address) == i::kClearedWeakHeapObjectLower32) {
return std::make_unique<ObjectPropertiesResult>(
d::TypeCheckResult::kWeakRef, "cleared weak ref", kHeapObject);
}
bool is_weak = (address & i::kHeapObjectTagMask) == i::kWeakHeapObjectTag;
if (is_weak) {
address &= ~i::kWeakHeapObjectMask;
}
if (i::Internals::HasHeapObjectTag(address)) {
std::unique_ptr<ObjectPropertiesResult> result =
GetHeapObjectPropertiesMaybeCompressed(address, memory_accessor,
heap_addresses, type_hint);
if (is_weak) {
result->Prepend("weak ref to ");
}
return result;
}
// For smi values, construct a response with a description representing the
// untagged value.
int32_t value = i::PlatformSmiTagging::SmiToInt(address);
std::stringstream stream;
stream << value << " (0x" << std::hex << value << ")";
return std::make_unique<ObjectPropertiesResult>(d::TypeCheckResult::kSmi,
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::Tagged<v8::internal::JSFunction>>(
"v8::internal::Tagged<v8::internal::JSFunction>"),
frame_pointer + StandardFrameConstants::kFunctionOffset, 1,
sizeof(v8::internal::JSFunction),
std::vector<std::unique_ptr<StructProperty>>(),
d::PropertyKind::kSingle));
// Add more items in the Locals pane representing the JS function name,
// source file name, and line & column numbers within the source file, so
// that the user doesn’t need to dig through the shared_function_info to
// find them.
intptr_t js_function_ptr = 0;
validity = memory_accessor(
frame_pointer + StandardFrameConstants::kFunctionOffset,
reinterpret_cast<void*>(&js_function_ptr), sizeof(intptr_t));
if (validity == d::MemoryAccessResult::kOk) {
TqJSFunction js_function(js_function_ptr);
auto shared_function_info_ptr =
js_function.GetSharedFunctionInfoValue(memory_accessor);
if (shared_function_info_ptr.validity == d::MemoryAccessResult::kOk) {
TqSharedFunctionInfo shared_function_info(
shared_function_info_ptr.value);
auto script_ptr =
shared_function_info.GetScriptValue(memory_accessor);
if (script_ptr.validity == d::MemoryAccessResult::kOk) {
// Make sure script_ptr is script.
auto address = script_ptr.value;
if (IsTypedHeapObjectInstanceTypeOf(address, memory_accessor,
i::InstanceType::SCRIPT_TYPE)) {
TqScript script(script_ptr.value);
props.push_back(std::make_unique<ObjectProperty>(
"script_name", kObjectAsStoredInHeap, script.GetNameAddress(),
1, i::kTaggedSize,
std::vector<std::unique_ptr<StructProperty>>(),
d::PropertyKind::kSingle));
props.push_back(std::make_unique<ObjectProperty>(
"script_source", kObjectAsStoredInHeap,
script.GetSourceAddress(), 1, i::kTaggedSize,
std::vector<std::unique_ptr<StructProperty>>(),
d::PropertyKind::kSingle));
}
}
auto name_or_scope_info_ptr =
shared_function_info.GetNameOrScopeInfoValue(memory_accessor);
if (name_or_scope_info_ptr.validity == d::MemoryAccessResult::kOk) {
auto scope_info_address = name_or_scope_info_ptr.value;
// Make sure name_or_scope_info_ptr is scope info.
if (IsTypedHeapObjectInstanceTypeOf(
scope_info_address, memory_accessor,
i::InstanceType::SCOPE_INFO_TYPE)) {
auto indexed_field_slice_function_variable_info =
TqDebugFieldSliceScopeInfoFunctionVariableInfo(
memory_accessor, scope_info_address);
if (indexed_field_slice_function_variable_info.validity ==
d::MemoryAccessResult::kOk) {
props.push_back(std::make_unique<ObjectProperty>(
"function_name", kObjectAsStoredInHeap,
scope_info_address - i::kHeapObjectTag +
std::get<1>(
indexed_field_slice_function_variable_info.value),
std::get<2>(
indexed_field_slice_function_variable_info.value),
i::kTaggedSize,
std::vector<std::unique_ptr<StructProperty>>(),
d::PropertyKind::kSingle));
}
std::vector<std::unique_ptr<StructProperty>>
position_info_struct_field_list;
position_info_struct_field_list.push_back(
std::make_unique<StructProperty>(
"start", kObjectAsStoredInHeap, 0, 0, 0));
position_info_struct_field_list.push_back(
std::make_unique<StructProperty>("end", kObjectAsStoredInHeap,
4, 0, 0));
TqScopeInfo scope_info(scope_info_address);
props.push_back(std::make_unique<ObjectProperty>(
"function_character_offset", "",
scope_info.GetPositionInfoAddress(), 1, 2 * i::kTaggedSize,
std::move(position_info_struct_field_list),
d::PropertyKind::kSingle));
}
}
}
}
}
}
return std::make_unique<StackFrameResult>(std::move(props));
}
} // namespace v8::internal::debug_helper_internal
namespace di = v8::internal::debug_helper_internal;
extern "C" {
V8_DEBUG_HELPER_EXPORT d::ObjectPropertiesResult*
_v8_debug_helper_GetObjectProperties(uintptr_t object,
d::MemoryAccessor memory_accessor,
const d::HeapAddresses& heap_addresses,
const char* type_hint) {
return di::GetObjectProperties(object, memory_accessor, heap_addresses,
type_hint)
.release()
->GetPublicView();
}
V8_DEBUG_HELPER_EXPORT void _v8_debug_helper_Free_ObjectPropertiesResult(
d::ObjectPropertiesResult* result) {
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);
}
}

View File

@ -0,0 +1,101 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "heap-constants.h"
#include "src/common/globals.h"
#include "src/heap/memory-chunk.h"
namespace d = v8::debug_helper;
namespace v8 {
namespace internal {
namespace debug_helper_internal {
std::string FindKnownObject(uintptr_t address,
const d::HeapAddresses& heap_addresses) {
uintptr_t containing_page = MemoryChunk::FromAddress(address)->address();
uintptr_t offset_in_page = MemoryChunk::AddressToOffset(address);
// If there's a match with a known page, then search only that page.
if (containing_page == heap_addresses.map_space_first_page) {
return FindKnownObjectInMapSpace(offset_in_page);
}
if (containing_page == heap_addresses.old_space_first_page) {
return FindKnownObjectInOldSpace(offset_in_page);
}
if (containing_page == heap_addresses.read_only_space_first_page) {
return FindKnownObjectInReadOnlySpace(offset_in_page);
}
// For any unknown pages, compile a list of things this object might be.
std::string result;
if (heap_addresses.map_space_first_page == 0) {
std::string sub_result = FindKnownObjectInMapSpace(offset_in_page);
if (!sub_result.empty()) {
result += "maybe " + sub_result;
}
}
if (heap_addresses.old_space_first_page == 0) {
std::string sub_result = FindKnownObjectInOldSpace(offset_in_page);
if (!sub_result.empty()) {
result = (result.empty() ? "" : result + ", ") + "maybe " + sub_result;
}
}
if (heap_addresses.read_only_space_first_page == 0) {
std::string sub_result = FindKnownObjectInReadOnlySpace(offset_in_page);
if (!sub_result.empty()) {
result = (result.empty() ? "" : result + ", ") + "maybe " + sub_result;
}
}
return result;
}
KnownInstanceType FindKnownMapInstanceTypes(
uintptr_t address, const d::HeapAddresses& heap_addresses) {
uintptr_t containing_page = MemoryChunk::FromAddress(address)->address();
uintptr_t offset_in_page = MemoryChunk::AddressToOffset(address);
// If there's a match with a known page, then search only that page.
if (containing_page == heap_addresses.map_space_first_page) {
return KnownInstanceType(
FindKnownMapInstanceTypeInMapSpace(offset_in_page));
}
if (containing_page == heap_addresses.old_space_first_page) {
return KnownInstanceType(
FindKnownMapInstanceTypeInOldSpace(offset_in_page));
}
if (containing_page == heap_addresses.read_only_space_first_page) {
return KnownInstanceType(
FindKnownMapInstanceTypeInReadOnlySpace(offset_in_page));
}
// For any unknown pages, compile a list of things this object might be.
KnownInstanceType result;
if (heap_addresses.map_space_first_page == 0) {
int sub_result = FindKnownMapInstanceTypeInMapSpace(offset_in_page);
if (sub_result >= 0) {
result.types.push_back(static_cast<i::InstanceType>(sub_result));
}
}
if (heap_addresses.old_space_first_page == 0) {
int sub_result = FindKnownMapInstanceTypeInOldSpace(offset_in_page);
if (sub_result >= 0) {
result.types.push_back(static_cast<i::InstanceType>(sub_result));
}
}
if (heap_addresses.read_only_space_first_page == 0) {
int sub_result = FindKnownMapInstanceTypeInReadOnlySpace(offset_in_page);
if (sub_result >= 0) {
result.types.push_back(static_cast<i::InstanceType>(sub_result));
}
}
return result;
}
} // namespace debug_helper_internal
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,72 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_TOOLS_DEBUG_HELPER_HEAP_CONSTANTS_H_
#define V8_TOOLS_DEBUG_HELPER_HEAP_CONSTANTS_H_
#include <cstdint>
#include <string>
#include <vector>
#include "debug-helper.h"
#include "src/objects/instance-type.h"
namespace d = v8::debug_helper;
namespace v8 {
namespace internal {
namespace debug_helper_internal {
// ===== Functions generated by gen-heap-constants.py: =========================
// Returns the name of a known object, given its offset within the first page of
// the space, or empty string on failure.
std::string FindKnownObjectInOldSpace(uintptr_t offset);
std::string FindKnownObjectInReadOnlySpace(uintptr_t offset);
std::string FindKnownObjectInMapSpace(uintptr_t offset);
// In builds with pointer compression enabled, sets the *_first_page members in
// the HeapAddresses object. In other builds, does nothing.
void FillInUnknownHeapAddresses(d::HeapAddresses* heap_addresses,
uintptr_t any_uncompressed_ptr);
// Returns the instance type for the known Map, given its offset within the
// first page of the space, or empty string on failure.
int FindKnownMapInstanceTypeInMapSpace(uintptr_t offset);
int FindKnownMapInstanceTypeInOldSpace(uintptr_t offset);
int FindKnownMapInstanceTypeInReadOnlySpace(uintptr_t offset);
// ===== End of generated functions. ===========================================
// Returns a descriptive string if the given address matches a known object, or
// an empty string otherwise.
std::string FindKnownObject(uintptr_t address,
const d::HeapAddresses& heap_addresses);
struct KnownInstanceType {
enum class Confidence {
kLow,
kHigh,
};
KnownInstanceType() : confidence(Confidence::kLow) {}
KnownInstanceType(int type) : KnownInstanceType() {
if (type >= 0) {
confidence = Confidence::kHigh;
types.push_back(static_cast<v8::internal::InstanceType>(type));
}
}
Confidence confidence;
std::vector<v8::internal::InstanceType> types;
};
// Returns information about the instance type of the Map at the given address,
// based on the list of known Maps.
KnownInstanceType FindKnownMapInstanceTypes(
uintptr_t address, const d::HeapAddresses& heap_addresses);
} // namespace debug_helper_internal
} // namespace internal
} // namespace v8
#endif