// 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 file defines the public interface to v8_debug_helper. #ifndef V8_TOOLS_DEBUG_HELPER_DEBUG_HELPER_H_ #define V8_TOOLS_DEBUG_HELPER_DEBUG_HELPER_H_ #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) #ifdef BUILDING_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; // In some cases, |type| may be a simple type representing a compressed // pointer such as v8::internal::TaggedValue. In those cases, // |decompressed_type| will contain the type of the object when decompressed. // Otherwise, |decompressed_type| will match |type|. In any case, it is safe // to pass the |decompressed_type| value as the type_hint on a subsequent call // to GetObjectProperties. const char* decompressed_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. The Isolate pointer is valid for this // purpose if you have it. uintptr_t any_heap_pointer; }; // Result type for ListObjectClasses. struct ClassList { size_t num_class_names; const char* const* class_names; // Fully qualified class names. }; } // 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 v8::debug_helper::ClassList* _v8_debug_helper_ListObjectClasses(); 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)); } // Get a list of all class names deriving from v8::internal::Object. inline const ClassList* ListObjectClasses() { return _v8_debug_helper_ListObjectClasses(); } // 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