This commit is the first step towards emitting unwinding information in

the .eh_frame format as part of the jitdump generated when
FLAG_perf_prof is enabled. The final goal is allowing precise unwinding
of callchains that include JITted code when profiling V8 using perf.

Unwinding information is stored in the body of code objects after the
code itself, prefixed with its length and aligned to a 8-byte boundary.
A boolean flag in the header signals its presence, resulting in zero
memory overhead when the generation of unwinding info is disabled or
no such information was attached to the code object.

A new jitdump record type (with id 4) is introduced for specifying
optional unwinding information for code load records. The EhFrameHdr
struct is also introduced, together with a constructor to initialise it
from the associated code object.

At this stage no unwinding information is written to the jitdump, but
the infrastructure for doing so is ready in place.

BUG=v8:4899
LOG=N

Review-Url: https://codereview.chromium.org/1993653003
Cr-Commit-Position: refs/heads/master@{#37296}
This commit is contained in:
ssanfilippo 2016-06-27 08:06:32 -07:00 committed by Commit bot
parent e1e50f3fff
commit 7d073b03c7
28 changed files with 528 additions and 12 deletions

View File

@ -1113,6 +1113,8 @@ v8_source_set("v8_base") {
"src/dtoa.cc",
"src/dtoa.h",
"src/effects.h",
"src/eh-frame.cc",
"src/eh-frame.h",
"src/elements-kind.cc",
"src/elements-kind.h",
"src/elements.cc",

View File

@ -517,6 +517,8 @@ void Assembler::GetCode(CodeDesc* desc) {
desc->constant_pool_size =
(constant_pool_offset ? desc->instr_size - constant_pool_offset : 0);
desc->origin = this;
desc->unwinding_info_size = 0;
desc->unwinding_info = nullptr;
}

View File

@ -603,6 +603,8 @@ void Assembler::GetCode(CodeDesc* desc) {
reloc_info_writer.pos());
desc->origin = this;
desc->constant_pool_size = 0;
desc->unwinding_info_size = 0;
desc->unwinding_info = nullptr;
}
}

96
src/eh-frame.cc Normal file
View File

@ -0,0 +1,96 @@
// Copyright 2016 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 "src/eh-frame.h"
#include "src/objects-inl.h"
#include "src/objects.h"
namespace v8 {
namespace internal {
static const int DW_EH_PE_pcrel = 0x10;
static const int DW_EH_PE_datarel = 0x30;
static const int DW_EH_PE_udata4 = 0x03;
static const int DW_EH_PE_sdata4 = 0x0b;
const int EhFrameHdr::kCIESize = 0;
static const int kVersionSize = 1;
static const int kEncodingSpecifiersSize = 3;
//
// In order to calculate offsets in the .eh_frame_hdr, we must know the layout
// of the DSO generated by perf inject, which is assumed to be the following:
//
// | ... | |
// +---------------+ <-- (F) --- | Larger offsets in file
// | | ^ |
// | Instructions | | .text v
// | | v
// +---------------+ <-- (E) ---
// |///////////////|
// |////Padding////|
// |///////////////|
// +---------------+ <-- (D) ---
// | | ^
// | CIE | |
// | | |
// +---------------+ <-- (C) | .eh_frame
// | | |
// | FDE | |
// | | v
// +---------------+ <-- (B) ---
// | version | ^
// +---------------+ |
// | encoding | |
// | specifiers | |
// +---------------+ <---(A) | .eh_frame_hdr
// | offset to | |
// | .eh_frame | |
// +---------------+ |
// | ... | ...
//
// (F) is aligned at a 16-byte boundary.
// (D) is aligned at a 8-byte boundary.
// (B) is aligned at a 4-byte boundary.
// (E), (C) and (A) have no alignment requirements.
//
// The distance between (A) and (B) is 4 bytes.
//
// The size of the .eh_frame is required to be a multiple of the pointer size,
// which means that (B) will be naturally aligned to a 4-byte boundary on all
// the architectures we support.
//
// Because (E) has no alignment requirements, there is padding between (E) and
// (D). (F) is aligned at a 16-byte boundary, thus to a 8-byte one as well.
//
EhFrameHdr::EhFrameHdr(Code* code) {
int code_size = code->is_crankshafted() ? code->safepoint_table_offset()
: code->instruction_size();
version_ = 1;
eh_frame_ptr_encoding_ = DW_EH_PE_sdata4 | DW_EH_PE_pcrel;
lut_size_encoding_ = DW_EH_PE_udata4;
lut_entries_encoding_ = DW_EH_PE_sdata4 | DW_EH_PE_datarel;
// .eh_frame pointer and LUT
if (code->has_unwinding_info()) {
DCHECK_GE(code->unwinding_info_size(), EhFrameHdr::kRecordSize);
int eh_frame_size = code->unwinding_info_size() - EhFrameHdr::kRecordSize;
offset_to_eh_frame_ =
-(eh_frame_size + kVersionSize + kEncodingSpecifiersSize); // A -> D
lut_entries_number_ = 1;
offset_to_procedure_ = -(RoundUp(code_size, 8) + eh_frame_size); // B -> F
offset_to_fde_ = -(eh_frame_size - kCIESize); // B -> C
} else {
// Create a dummy table
offset_to_eh_frame_ = 0;
lut_entries_number_ = 0;
offset_to_procedure_ = 0;
offset_to_fde_ = 0;
}
}
} // namespace internal
} // namespace v8

41
src/eh-frame.h Normal file
View File

@ -0,0 +1,41 @@
// Copyright 2016 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_EH_FRAME_H_
#define V8_EH_FRAME_H_
#include <cstdint>
namespace v8 {
namespace internal {
class Code;
class EhFrameHdr final {
public:
static const int kRecordSize = 20;
static const int kCIESize;
explicit EhFrameHdr(Code* code);
int32_t offset_to_eh_frame() const { return offset_to_eh_frame_; }
uint32_t lut_entries_number() const { return lut_entries_number_; }
int32_t offset_to_procedure() const { return offset_to_procedure_; }
int32_t offset_to_fde() const { return offset_to_fde_; }
private:
uint8_t version_;
uint8_t eh_frame_ptr_encoding_;
uint8_t lut_size_encoding_;
uint8_t lut_entries_encoding_;
int32_t offset_to_eh_frame_;
uint32_t lut_entries_number_;
int32_t offset_to_procedure_;
int32_t offset_to_fde_;
};
} // namespace internal
} // namespace v8
#endif

View File

@ -1400,9 +1400,18 @@ Handle<Code> Factory::NewCode(const CodeDesc& desc,
bool is_debug) {
Handle<ByteArray> reloc_info = NewByteArray(desc.reloc_size, TENURED);
bool has_unwinding_info = desc.unwinding_info != nullptr;
DCHECK((has_unwinding_info && desc.unwinding_info_size > 0) ||
(!has_unwinding_info && desc.unwinding_info_size == 0));
// Compute size.
int body_size = RoundUp(desc.instr_size, kObjectAlignment);
int obj_size = Code::SizeFor(body_size);
int body_size = desc.instr_size;
int unwinding_info_size_field_size = kInt64Size;
if (has_unwinding_info) {
body_size = RoundUp(body_size, kInt64Size) + desc.unwinding_info_size +
unwinding_info_size_field_size;
}
int obj_size = Code::SizeFor(RoundUp(body_size, kObjectAlignment));
Handle<Code> code = NewCodeRaw(obj_size, immovable);
DCHECK(!isolate()->heap()->memory_allocator()->code_range()->valid() ||
@ -1418,6 +1427,7 @@ Handle<Code> Factory::NewCode(const CodeDesc& desc,
code->set_instruction_size(desc.instr_size);
code->set_relocation_info(*reloc_info);
code->set_flags(flags);
code->set_has_unwinding_info(has_unwinding_info);
code->set_raw_kind_specific_flags1(0);
code->set_raw_kind_specific_flags2(0);
code->set_is_crankshafted(crankshafted);

View File

@ -1053,6 +1053,8 @@ DEFINE_BOOL(perf_prof, false,
DEFINE_NEG_IMPLICATION(perf_prof, compact_code_space)
DEFINE_BOOL(perf_prof_debug_info, false,
"Enable debug info for perf linux profiler (experimental).")
DEFINE_BOOL(perf_prof_unwinding_info, false,
"Enable unwinding info for perf linux profiler (experimental).")
DEFINE_STRING(gc_fake_mmap, "/tmp/__v8_gc__",
"Specify the name of the file for fake gc mmap used in ll_prof")
DEFINE_BOOL(log_internal_timer_events, false, "Time internal events.")

View File

@ -577,6 +577,8 @@ struct CodeDesc {
int instr_size;
int reloc_size;
int constant_pool_size;
byte* unwinding_info;
int unwinding_info_size;
Assembler* origin;
};

View File

@ -332,6 +332,8 @@ void Assembler::GetCode(CodeDesc* desc) {
desc->reloc_size = (buffer_ + buffer_size_) - reloc_info_writer.pos();
desc->origin = this;
desc->constant_pool_size = 0;
desc->unwinding_info_size = 0;
desc->unwinding_info = nullptr;
}

View File

@ -319,6 +319,8 @@ void Assembler::GetCode(CodeDesc* desc) {
desc->reloc_size = (buffer_ + buffer_size_) - reloc_info_writer.pos();
desc->origin = this;
desc->constant_pool_size = 0;
desc->unwinding_info_size = 0;
desc->unwinding_info = nullptr;
}

View File

@ -300,6 +300,8 @@ void Assembler::GetCode(CodeDesc* desc) {
static_cast<int>((buffer_ + buffer_size_) - reloc_info_writer.pos());
desc->origin = this;
desc->constant_pool_size = 0;
desc->unwinding_info_size = 0;
desc->unwinding_info = nullptr;
}

View File

@ -4891,6 +4891,16 @@ inline bool Code::is_interpreter_trampoline_builtin() {
this == *builtins->InterpreterMarkBaselineOnReturn();
}
inline bool Code::has_unwinding_info() const {
return HasUnwindingInfoField::decode(READ_UINT32_FIELD(this, kFlagsOffset));
}
inline void Code::set_has_unwinding_info(bool state) {
uint32_t previous = READ_UINT32_FIELD(this, kFlagsOffset);
uint32_t updated_value = HasUnwindingInfoField::update(previous, state);
WRITE_UINT32_FIELD(this, kFlagsOffset, updated_value);
}
inline void Code::set_is_crankshafted(bool value) {
int previous = READ_UINT32_FIELD(this, kKindSpecificFlags2Offset);
int updated = IsCrankshaftedField::update(previous, value);
@ -6439,7 +6449,6 @@ ACCESSORS(Code, deoptimization_data, FixedArray, kDeoptimizationDataOffset)
ACCESSORS(Code, raw_type_feedback_info, Object, kTypeFeedbackInfoOffset)
ACCESSORS(Code, next_code_link, Object, kNextCodeLinkOffset)
void Code::WipeOutHeader() {
WRITE_FIELD(this, kRelocationInfoOffset, NULL);
WRITE_FIELD(this, kHandlerTableOffset, NULL);
@ -6493,9 +6502,38 @@ byte* Code::instruction_end() {
return instruction_start() + instruction_size();
}
int Code::GetUnwindingInfoSizeOffset() const {
DCHECK(has_unwinding_info());
return RoundUp(kHeaderSize + instruction_size(), kInt64Size);
}
int Code::unwinding_info_size() const {
DCHECK(has_unwinding_info());
return static_cast<int>(
READ_UINT64_FIELD(this, GetUnwindingInfoSizeOffset()));
}
void Code::set_unwinding_info_size(int value) {
DCHECK(has_unwinding_info());
WRITE_UINT64_FIELD(this, GetUnwindingInfoSizeOffset(), value);
}
byte* Code::unwinding_info_start() {
DCHECK(has_unwinding_info());
return FIELD_ADDR(this, GetUnwindingInfoSizeOffset()) + kInt64Size;
}
byte* Code::unwinding_info_end() {
DCHECK(has_unwinding_info());
return unwinding_info_start() + unwinding_info_size();
}
int Code::body_size() {
return RoundUp(instruction_size(), kObjectAlignment);
int unpadded_body_size =
has_unwinding_info()
? static_cast<int>(unwinding_info_end() - instruction_start())
: instruction_size();
return RoundUp(unpadded_body_size, kObjectAlignment);
}
int Code::SizeIncludingMetadata() {

View File

@ -13538,6 +13538,14 @@ void Code::CopyFrom(const CodeDesc& desc) {
CopyBytes(instruction_start(), desc.buffer,
static_cast<size_t>(desc.instr_size));
// copy unwinding info, if any
if (desc.unwinding_info) {
DCHECK_GT(desc.unwinding_info_size, 0);
set_unwinding_info_size(desc.unwinding_info_size);
CopyBytes(unwinding_info_start(), desc.unwinding_info,
static_cast<size_t>(desc.unwinding_info_size));
}
// copy reloc info
CopyBytes(relocation_start(),
desc.buffer + desc.buffer_size - desc.reloc_size,

View File

@ -5143,7 +5143,8 @@ class Code: public HeapObject {
// Returns the address right after the last instruction.
inline byte* instruction_end();
// Returns the size of the instructions, padding, and relocation information.
// Returns the size of the instructions, padding, relocation and unwinding
// information.
inline int body_size();
// Returns the size of code and its metadata. This includes the size of code
@ -5153,6 +5154,49 @@ class Code: public HeapObject {
// Returns the address of the first relocation info (read backwards!).
inline byte* relocation_start();
// [has_unwinding_info]: Whether this code object has unwinding information.
// If it doesn't, unwinding_information_start() will point to invalid data.
//
// The body of all code objects has the following layout.
//
// +--------------------------+ <-- instruction_start()
// | instructions |
// | ... |
// +--------------------------+
// | relocation info |
// | ... |
// +--------------------------+ <-- instruction_end()
//
// If has_unwinding_info() is false, instruction_end() points to the first
// memory location after the end of the code object. Otherwise, the body
// continues as follows:
//
// +--------------------------+
// | padding to the next |
// | 8-byte aligned address |
// +--------------------------+ <-- instruction_end()
// | [unwinding_info_size] |
// | as uint64_t |
// +--------------------------+ <-- unwinding_info_start()
// | unwinding info |
// | ... |
// +--------------------------+ <-- unwinding_info_end()
//
// and unwinding_info_end() points to the first memory location after the end
// of the code object.
//
DECL_BOOLEAN_ACCESSORS(has_unwinding_info)
// [unwinding_info_size]: Size of the unwinding information.
inline int unwinding_info_size() const;
inline void set_unwinding_info_size(int value);
// Returns the address of the unwinding information, if any.
inline byte* unwinding_info_start();
// Returns the address right after the end of the unwinding information.
inline byte* unwinding_info_end();
// Code entry point.
inline byte* entry();
@ -5283,6 +5327,8 @@ class Code: public HeapObject {
static const int kHeaderSize =
(kHeaderPaddingStart + kCodeAlignmentMask) & ~kCodeAlignmentMask;
inline int GetUnwindingInfoSizeOffset() const;
class BodyDescriptor;
// Byte offsets within kKindSpecificFlags1Offset.
@ -5301,9 +5347,11 @@ class Code: public HeapObject {
: public BitField<CacheHolderFlag, ICStateField::kNext, 2> {};
class KindField : public BitField<Kind, CacheHolderField::kNext, 5> {};
STATIC_ASSERT(NUMBER_OF_KINDS <= KindField::kMax);
class ExtraICStateField : public BitField<ExtraICState, KindField::kNext,
PlatformSmiTagging::kSmiValueSize -
KindField::kNext + 1> {};
class HasUnwindingInfoField : public BitField<bool, KindField::kNext, 1> {};
class ExtraICStateField
: public BitField<ExtraICState, HasUnwindingInfoField::kNext,
PlatformSmiTagging::kSmiValueSize -
HasUnwindingInfoField::kNext + 1> {};
// KindSpecificFlags1 layout (STUB, BUILTIN and OPTIMIZED_FUNCTION)
static const int kStackSlotsFirstBit = 0;

View File

@ -28,6 +28,7 @@
#include "src/perf-jit.h"
#include "src/assembler.h"
#include "src/eh-frame.h"
#include "src/objects-inl.h"
#if V8_OS_LINUX
@ -56,7 +57,13 @@ struct PerfJitHeader {
};
struct PerfJitBase {
enum PerfJitEvent { kLoad = 0, kMove = 1, kDebugInfo = 2, kClose = 3 };
enum PerfJitEvent {
kLoad = 0,
kMove = 1,
kDebugInfo = 2,
kClose = 3,
kUnwindingInfo = 4
};
uint32_t event_;
uint32_t size_;
@ -85,6 +92,13 @@ struct PerfJitCodeDebugInfo : PerfJitBase {
// Followed by entry_count_ instances of PerfJitDebugEntry.
};
struct PerfJitCodeUnwindingInfo : PerfJitBase {
uint64_t unwinding_size_;
uint64_t eh_frame_hdr_size_;
uint64_t mapped_size_;
// Followed by size_ - sizeof(PerfJitCodeUnwindingInfo) bytes of data.
};
const char PerfJitLogger::kFilenameFormatString[] = "./jit-%d.dump";
// Extra padding for the PID in the filename
@ -204,6 +218,9 @@ void PerfJitLogger::LogRecordedBuffer(AbstractCode* abstract_code,
uint32_t code_size = code->is_crankshafted() ? code->safepoint_table_offset()
: code->instruction_size();
// Unwinding info comes right after debug info.
if (FLAG_perf_prof_unwinding_info) LogWriteUnwindingInfo(code);
static const char string_terminator[] = "\0";
PerfJitCodeLoad code_load;
@ -303,6 +320,46 @@ void PerfJitLogger::LogWriteDebugInfo(Code* code, SharedFunctionInfo* shared) {
LogWriteBytes(padding_bytes, padding);
}
void PerfJitLogger::LogWriteUnwindingInfo(Code* code) {
EhFrameHdr eh_frame_hdr(code);
PerfJitCodeUnwindingInfo unwinding_info_header;
unwinding_info_header.event_ = PerfJitCodeLoad::kUnwindingInfo;
unwinding_info_header.time_stamp_ = GetTimestamp();
unwinding_info_header.eh_frame_hdr_size_ = EhFrameHdr::kRecordSize;
if (code->has_unwinding_info()) {
unwinding_info_header.unwinding_size_ = code->unwinding_info_size();
unwinding_info_header.mapped_size_ = unwinding_info_header.unwinding_size_;
} else {
unwinding_info_header.unwinding_size_ = EhFrameHdr::kRecordSize;
unwinding_info_header.mapped_size_ = 0;
}
int content_size = static_cast<int>(sizeof(unwinding_info_header) +
unwinding_info_header.unwinding_size_);
int padding_size = RoundUp(content_size, 8) - content_size;
unwinding_info_header.size_ = content_size + padding_size;
LogWriteBytes(reinterpret_cast<const char*>(&unwinding_info_header),
sizeof(unwinding_info_header));
if (code->has_unwinding_info()) {
// The last EhFrameHdr::kRecordSize bytes were a placeholder for the header.
// Discard them and write the actual eh_frame_hdr (below).
DCHECK_GE(code->unwinding_info_size(), EhFrameHdr::kRecordSize);
LogWriteBytes(reinterpret_cast<const char*>(code->unwinding_info_start()),
code->unwinding_info_size() - EhFrameHdr::kRecordSize);
}
LogWriteBytes(reinterpret_cast<const char*>(&eh_frame_hdr),
EhFrameHdr::kRecordSize);
char padding_bytes[] = "\0\0\0\0\0\0\0\0";
DCHECK_LT(padding_size, sizeof(padding_bytes));
LogWriteBytes(padding_bytes, padding_size);
}
void PerfJitLogger::CodeMoveEvent(AbstractCode* from, Address to) {
// Code relocation not supported.
UNREACHABLE();

View File

@ -66,6 +66,7 @@ class PerfJitLogger : public CodeEventLogger {
void LogWriteBytes(const char* bytes, int size);
void LogWriteHeader();
void LogWriteDebugInfo(Code* code, SharedFunctionInfo* shared);
void LogWriteUnwindingInfo(Code* code);
static const uint32_t kElfMachIA32 = 3;
static const uint32_t kElfMachX64 = 62;

View File

@ -257,6 +257,8 @@ void Assembler::GetCode(CodeDesc* desc) {
desc->constant_pool_size =
(constant_pool_offset ? desc->instr_size - constant_pool_offset : 0);
desc->origin = this;
desc->unwinding_info_size = 0;
desc->unwinding_info = nullptr;
}

View File

@ -300,6 +300,8 @@ void Assembler::GetCode(CodeDesc* desc) {
desc->instr_size = pc_offset();
desc->reloc_size = (buffer_ + buffer_size_) - reloc_info_writer.pos();
desc->origin = this;
desc->unwinding_info_size = 0;
desc->unwinding_info = nullptr;
}
void Assembler::Align(int m) {

View File

@ -783,6 +783,8 @@
'dtoa.cc',
'dtoa.h',
'effects.h',
'eh-frame.cc',
'eh-frame.h',
'elements-kind.cc',
'elements-kind.h',
'elements.cc',

View File

@ -265,7 +265,7 @@ Handle<Code> CreatePlaceholder(Factory* factory, uint32_t index,
// the {constant_pool_offset} field of the code object.
// TODO(titzer): placeholder code objects are somewhat dangerous.
static byte buffer[] = {0, 0, 0, 0, 0, 0, 0, 0}; // fake instructions.
static CodeDesc desc = {buffer, 8, 8, 0, 0, nullptr};
static CodeDesc desc = {buffer, 8, 8, 0, 0, nullptr, 0, nullptr};
Handle<Code> code = factory->NewCode(desc, Code::KindField::encode(kind),
Handle<Object>::null());
code->set_constant_pool_offset(static_cast<int>(index) + kPlaceholderMarker);

View File

@ -320,6 +320,8 @@ void Assembler::GetCode(CodeDesc* desc) {
static_cast<int>((buffer_ + buffer_size_) - reloc_info_writer.pos());
desc->origin = this;
desc->constant_pool_size = 0;
desc->unwinding_info_size = 0;
desc->unwinding_info = nullptr;
}

View File

@ -277,6 +277,8 @@ void Assembler::GetCode(CodeDesc* desc) {
desc->reloc_size = (buffer_ + buffer_size_) - reloc_info_writer.pos();
desc->origin = this;
desc->constant_pool_size = 0;
desc->unwinding_info_size = 0;
desc->unwinding_info = nullptr;
}

View File

@ -101,6 +101,7 @@ v8_executable("cctest") {
"test-bit-vector.cc",
"test-circular-queue.cc",
"test-code-cache.cc",
"test-code-layout.cc",
"test-code-stub-assembler.cc",
"test-compiler.cc",
"test-constantpool.cc",
@ -114,6 +115,7 @@ v8_executable("cctest") {
"test-diy-fp.cc",
"test-double.cc",
"test-dtoa.cc",
"test-eh-frame-hdr.cc",
"test-elements-kind.cc",
"test-fast-dtoa.cc",
"test-feedback-vector.cc",

View File

@ -136,6 +136,7 @@
'test-bit-vector.cc',
'test-circular-queue.cc',
'test-code-cache.cc',
'test-code-layout.cc',
'test-code-stub-assembler.cc',
'test-compiler.cc',
'test-constantpool.cc',
@ -150,6 +151,7 @@
'test-double.cc',
'test-dtoa.cc',
'test-elements-kind.cc',
'test-eh-frame-hdr.cc',
'test-fast-dtoa.cc',
'test-feedback-vector.cc',
'test-field-type-tracking.cc',

View File

@ -19,6 +19,8 @@ static Handle<Code> GetDummyCode(Isolate* isolate) {
0, // instr_size
0, // reloc_size
0, // constant_pool_size
nullptr, // unwinding_info
0, // unwinding_info_size
nullptr}; // origin
Code::Flags flags =
Code::ComputeFlags(Code::LOAD_IC, kNoExtraICState, kCacheOnReceiver);

View File

@ -0,0 +1,84 @@
// Copyright 2016 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 "src/objects.h"
#include "test/cctest/cctest.h"
using namespace v8::internal;
TEST(CodeLayoutWithoutUnwindingInfo) {
CcTest::InitializeVM();
HandleScope handle_scope(CcTest::i_isolate());
// "Hello, World!" in ASCII.
byte buffer_array[13] = {0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20,
0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21};
byte* buffer = &buffer_array[0];
int buffer_size = sizeof(buffer_array);
CodeDesc code_desc;
code_desc.buffer = buffer;
code_desc.buffer_size = buffer_size;
code_desc.constant_pool_size = 0;
code_desc.instr_size = buffer_size;
code_desc.reloc_size = 0;
code_desc.origin = nullptr;
code_desc.unwinding_info = nullptr;
code_desc.unwinding_info_size = 0;
Handle<Code> code = CcTest::i_isolate()->factory()->NewCode(
code_desc, 0, Handle<Object>::null());
CHECK(!code->has_unwinding_info());
CHECK_EQ(code->instruction_size(), buffer_size);
CHECK_EQ(memcmp(code->instruction_start(), buffer, buffer_size), 0);
CHECK_EQ(code->instruction_end() - reinterpret_cast<byte*>(*code),
Code::kHeaderSize + buffer_size - kHeapObjectTag);
}
TEST(CodeLayoutWithUnwindingInfo) {
CcTest::InitializeVM();
HandleScope handle_scope(CcTest::i_isolate());
// "Hello, World!" in ASCII.
byte buffer_array[13] = {0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20,
0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21};
// "JavaScript" in ASCII.
byte unwinding_info_array[10] = {0x4a, 0x61, 0x76, 0x61, 0x53,
0x63, 0x72, 0x69, 0x70, 0x74};
byte* buffer = &buffer_array[0];
int buffer_size = sizeof(buffer_array);
byte* unwinding_info = &unwinding_info_array[0];
int unwinding_info_size = sizeof(unwinding_info_array);
CodeDesc code_desc;
code_desc.buffer = buffer;
code_desc.buffer_size = buffer_size;
code_desc.constant_pool_size = 0;
code_desc.instr_size = buffer_size;
code_desc.reloc_size = 0;
code_desc.origin = nullptr;
code_desc.unwinding_info = unwinding_info;
code_desc.unwinding_info_size = unwinding_info_size;
Handle<Code> code = CcTest::i_isolate()->factory()->NewCode(
code_desc, 0, Handle<Object>::null());
CHECK(code->has_unwinding_info());
CHECK_EQ(code->instruction_size(), buffer_size);
CHECK_EQ(memcmp(code->instruction_start(), buffer, buffer_size), 0);
CHECK(IsAligned(code->GetUnwindingInfoSizeOffset(), 8));
CHECK_EQ(code->unwinding_info_size(), unwinding_info_size);
CHECK(
IsAligned(reinterpret_cast<uintptr_t>(code->unwinding_info_start()), 8));
CHECK_EQ(
memcmp(code->unwinding_info_start(), unwinding_info, unwinding_info_size),
0);
CHECK_EQ(code->unwinding_info_end() - reinterpret_cast<byte*>(*code),
Code::kHeaderSize + RoundUp(buffer_size, kInt64Size) + kInt64Size +
unwinding_info_size - kHeapObjectTag);
}

View File

@ -0,0 +1,100 @@
// Copyright 2016 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 "src/eh-frame.h"
#include "src/objects.h"
#include "test/cctest/cctest.h"
using namespace v8::internal;
TEST(EhFrameHdr) {
CcTest::InitializeVM();
HandleScope handle_scope(CcTest::i_isolate());
// The content is not relevant in this test
byte buffer[10] = {0};
byte unwinding_info[30 + EhFrameHdr::kRecordSize] = {0};
CodeDesc code_desc;
code_desc.buffer = &buffer[0];
code_desc.buffer_size = sizeof(buffer);
code_desc.constant_pool_size = 0;
code_desc.instr_size = sizeof(buffer);
code_desc.reloc_size = 0;
code_desc.origin = nullptr;
code_desc.unwinding_info = &unwinding_info[0];
code_desc.unwinding_info_size = sizeof(unwinding_info);
Handle<Code> code = CcTest::i_isolate()->factory()->NewCode(
code_desc, 0, Handle<Object>::null());
EhFrameHdr eh_frame_hdr(*code);
CHECK_EQ(eh_frame_hdr.lut_entries_number(), 1);
//
// Plugging some numbers in the DSO layout shown in eh-frame.cc:
//
// | ... |
// +---------------+ <-- (E) --------
// | | ^
// | Instructions | 10 bytes | .text
// | | v
// +---------------+ <---------------
// |///////////////|
// |////Padding////| 6 bytes
// |///////////////|
// +---------------+ <---(D)---------
// | | ^
// | CIE | N bytes* |
// | | |
// +---------------+ <-- (C) | .eh_frame
// | | |
// | FDE | 30 - N bytes |
// | | v
// +---------------+ <-- (B) --------
// | version | ^
// +---------------+ 4 bytes |
// | encoding | |
// | specifiers | |
// +---------------+ <---(A) | .eh_frame_hdr
// | offset to | |
// | .eh_frame | |
// +---------------+ |
// | ... | ...
//
// (*) the size of the CIE is platform dependent.
//
CHECK_EQ(eh_frame_hdr.offset_to_eh_frame(), -(4 + 30)); // A -> D
CHECK_EQ(eh_frame_hdr.offset_to_procedure(), -(30 + 6 + 10)); // B -> E
CHECK_EQ(eh_frame_hdr.offset_to_fde(),
-(30 - EhFrameHdr::kCIESize)); // B -> C
}
TEST(DummyEhFrameHdr) {
CcTest::InitializeVM();
HandleScope handle_scope(CcTest::i_isolate());
byte buffer[10] = {0}; // The content is not relevant in this test
CodeDesc code_desc;
code_desc.buffer = &buffer[0];
code_desc.buffer_size = sizeof(buffer);
code_desc.constant_pool_size = 0;
code_desc.instr_size = sizeof(buffer);
code_desc.reloc_size = 0;
code_desc.origin = nullptr;
code_desc.unwinding_info = nullptr;
code_desc.unwinding_info_size = 0;
Handle<Code> code = CcTest::i_isolate()->factory()->NewCode(
code_desc, 0, Handle<Object>::null());
EhFrameHdr eh_frame_hdr(*code);
// A dummy header has an empty LUT
CHECK_EQ(eh_frame_hdr.lut_entries_number(), 0);
// These values should be irrelevant, but check that they have been zeroed.
CHECK_EQ(eh_frame_hdr.offset_to_eh_frame(), 0);
CHECK_EQ(eh_frame_hdr.offset_to_procedure(), 0);
CHECK_EQ(eh_frame_hdr.offset_to_fde(), 0);
}

View File

@ -68,8 +68,9 @@ TEST(Positions) {
writer.Finish();
relocation_info_size = static_cast<int>(buffer_end - writer.pos());
MacroAssembler assm(CcTest::i_isolate(), nullptr, 0, CodeObjectRequired::kNo);
CodeDesc desc = {buffer.get(), buffer_size, code_size,
relocation_info_size, 0, &assm};
CodeDesc desc = {
buffer.get(), buffer_size, code_size, relocation_info_size, 0,
nullptr, 0, &assm};
// Read only (non-statement) positions.
{