mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 02:47:36 +00:00
[vm] When generating JITDUMP also dump IR and attach it as source to code.
This allows to see IR intermixed with code in perf-annotate. Change-Id: Ib7261df2cb789bfb6e3854a32d5300d0c27302ba Reviewed-on: https://dart-review.googlesource.com/66575 Commit-Queue: Vyacheslav Egorov <vegorov@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com>
This commit is contained in:
parent
24158b773d
commit
beaf05efa3
|
@ -29,11 +29,13 @@ void CodeObservers::NotifyAll(const char* name,
|
|||
uword base,
|
||||
uword prologue_offset,
|
||||
uword size,
|
||||
bool optimized) {
|
||||
bool optimized,
|
||||
const CodeComments* comments) {
|
||||
ASSERT(!AreActive() || (strlen(name) != 0));
|
||||
for (intptr_t i = 0; i < observers_length_; i++) {
|
||||
if (observers_[i]->IsActive()) {
|
||||
observers_[i]->Notify(name, base, prologue_offset, size, optimized);
|
||||
observers_[i]->Notify(name, base, prologue_offset, size, optimized,
|
||||
comments);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,18 @@ namespace dart {
|
|||
|
||||
class Mutex;
|
||||
|
||||
// An abstract representation of comments associated with the given code
|
||||
// object. We assume that comments are sorted by PCOffset.
|
||||
class CodeComments : public ValueObject {
|
||||
public:
|
||||
CodeComments() = default;
|
||||
virtual ~CodeComments() = default;
|
||||
|
||||
virtual intptr_t Length() const = 0;
|
||||
virtual intptr_t PCOffsetAt(intptr_t index) const = 0;
|
||||
virtual const char* CommentAt(intptr_t index) const = 0;
|
||||
};
|
||||
|
||||
// Object observing code creation events. Used by external profilers and
|
||||
// debuggers to map address ranges to function names.
|
||||
class CodeObserver {
|
||||
|
@ -32,7 +44,8 @@ class CodeObserver {
|
|||
uword base,
|
||||
uword prologue_offset,
|
||||
uword size,
|
||||
bool optimized) = 0;
|
||||
bool optimized,
|
||||
const CodeComments* comments) = 0;
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(CodeObserver);
|
||||
|
@ -49,7 +62,8 @@ class CodeObservers : public AllStatic {
|
|||
uword base,
|
||||
uword prologue_offset,
|
||||
uword size,
|
||||
bool optimized);
|
||||
bool optimized,
|
||||
const CodeComments* comments);
|
||||
|
||||
// Returns true if there is at least one active code observer.
|
||||
static bool AreActive();
|
||||
|
|
|
@ -14895,6 +14895,29 @@ RawCode* Code::New(intptr_t pointer_offsets_length) {
|
|||
}
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
#if !defined(PRODUCT)
|
||||
class CodeCommentsWrapper final : public CodeComments {
|
||||
public:
|
||||
explicit CodeCommentsWrapper(const Code::Comments& comments)
|
||||
: comments_(comments), string_(String::Handle()) {}
|
||||
|
||||
intptr_t Length() const override { return comments_.Length(); }
|
||||
|
||||
intptr_t PCOffsetAt(intptr_t i) const override {
|
||||
return comments_.PCOffsetAt(i);
|
||||
}
|
||||
|
||||
const char* CommentAt(intptr_t i) const override {
|
||||
string_ = comments_.CommentAt(i);
|
||||
return string_.ToCString();
|
||||
}
|
||||
|
||||
private:
|
||||
const Code::Comments& comments_;
|
||||
String& string_;
|
||||
};
|
||||
#endif
|
||||
|
||||
RawCode* Code::FinalizeCode(const char* name,
|
||||
Assembler* assembler,
|
||||
bool optimized,
|
||||
|
@ -14935,11 +14958,14 @@ RawCode* Code::FinalizeCode(const char* name,
|
|||
}
|
||||
#endif
|
||||
|
||||
const Code::Comments& comments = assembler->GetCodeComments();
|
||||
|
||||
code.set_compile_timestamp(OS::GetCurrentMonotonicMicros());
|
||||
#ifndef PRODUCT
|
||||
CodeCommentsWrapper comments_wrapper(comments);
|
||||
CodeObservers::NotifyAll(name, instrs.PayloadStart(),
|
||||
assembler->prologue_offset(), instrs.Size(),
|
||||
optimized);
|
||||
optimized, &comments_wrapper);
|
||||
#endif
|
||||
{
|
||||
NoSafepointScope no_safepoint;
|
||||
|
@ -14975,7 +15001,7 @@ RawCode* Code::FinalizeCode(const char* name,
|
|||
instrs.raw()->Size(), VirtualMemory::kReadExecute);
|
||||
}
|
||||
}
|
||||
code.set_comments(assembler->GetCodeComments());
|
||||
code.set_comments(comments);
|
||||
if (assembler->prologue_offset() >= 0) {
|
||||
code.SetPrologueOffset(assembler->prologue_offset());
|
||||
} else {
|
||||
|
@ -15033,7 +15059,7 @@ RawCode* Code::FinalizeBytecode(const void* bytecode_data,
|
|||
#ifndef PRODUCT
|
||||
CodeObservers::NotifyAll("bytecode", instrs.PayloadStart(),
|
||||
0 /* prologue_offset */, instrs.Size(),
|
||||
false /* optimized */);
|
||||
false /* optimized */, nullptr);
|
||||
#endif
|
||||
{
|
||||
NoSafepointScope no_safepoint;
|
||||
|
|
|
@ -64,7 +64,8 @@ class PerfCodeObserver : public CodeObserver {
|
|||
uword base,
|
||||
uword prologue_offset,
|
||||
uword size,
|
||||
bool optimized) {
|
||||
bool optimized,
|
||||
const CodeComments* comments) {
|
||||
Dart_FileWriteCallback file_write = Dart::file_write_callback();
|
||||
if ((file_write == NULL) || (out_file_ == NULL)) {
|
||||
return;
|
||||
|
|
|
@ -46,6 +46,9 @@ DEFINE_FLAG(bool,
|
|||
|
||||
DECLARE_FLAG(bool, write_protect_code);
|
||||
DECLARE_FLAG(bool, write_protect_vm_isolate);
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
DECLARE_FLAG(bool, code_comments);
|
||||
#endif
|
||||
|
||||
// Linux CodeObservers.
|
||||
|
||||
|
@ -84,7 +87,8 @@ class PerfCodeObserver : public CodeObserver {
|
|||
uword base,
|
||||
uword prologue_offset,
|
||||
uword size,
|
||||
bool optimized) {
|
||||
bool optimized,
|
||||
const CodeComments* comments) {
|
||||
Dart_FileWriteCallback file_write = Dart::file_write_callback();
|
||||
if ((file_write == NULL) || (out_file_ == NULL)) {
|
||||
return;
|
||||
|
@ -119,10 +123,8 @@ class PerfCodeObserver : public CodeObserver {
|
|||
// JITDUMP binary format.
|
||||
class JitDumpCodeObserver : public CodeObserver {
|
||||
public:
|
||||
JitDumpCodeObserver()
|
||||
: out_file_(nullptr), mapped_(nullptr), mapped_size_(0), code_id_(0) {
|
||||
const intptr_t pid = getpid();
|
||||
char* const filename = OS::SCreate(nullptr, "/tmp/jit-%" Pd ".dump", pid);
|
||||
JitDumpCodeObserver() : pid_(getpid()) {
|
||||
char* const filename = OS::SCreate(nullptr, "/tmp/jit-%" Pd ".dump", pid_);
|
||||
const int fd = open(filename, O_CREAT | O_TRUNC | O_RDWR, 0666);
|
||||
free(filename);
|
||||
|
||||
|
@ -162,6 +164,11 @@ class JitDumpCodeObserver : public CodeObserver {
|
|||
FLAG_write_protect_code = false;
|
||||
FLAG_write_protect_vm_isolate = false;
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
// Enable code comments.
|
||||
FLAG_code_comments = true;
|
||||
#endif
|
||||
|
||||
// Write JITDUMP header.
|
||||
WriteHeader();
|
||||
}
|
||||
|
@ -186,11 +193,16 @@ class JitDumpCodeObserver : public CodeObserver {
|
|||
uword base,
|
||||
uword prologue_offset,
|
||||
uword size,
|
||||
bool optimized) {
|
||||
bool optimized,
|
||||
const CodeComments* comments) {
|
||||
MutexLocker ml(CodeObservers::mutex());
|
||||
|
||||
const char* marker = optimized ? "*" : "";
|
||||
char* buffer = OS::SCreate(Thread::Current()->zone(), "%s%s", marker, name);
|
||||
const size_t name_length = strlen(buffer);
|
||||
|
||||
WriteDebugInfo(base, comments);
|
||||
|
||||
CodeLoadEvent ev;
|
||||
ev.event = BaseEvent::kLoad;
|
||||
ev.size = sizeof(ev) + (name_length + 1) + size;
|
||||
|
@ -200,15 +212,11 @@ class JitDumpCodeObserver : public CodeObserver {
|
|||
ev.vma = base;
|
||||
ev.code_address = base;
|
||||
ev.code_size = size;
|
||||
ev.code_id = code_id_++;
|
||||
|
||||
{
|
||||
MutexLocker ml(CodeObservers::mutex());
|
||||
ev.code_id = code_id_++;
|
||||
|
||||
WriteFully(&ev, sizeof(ev));
|
||||
WriteFully(buffer, name_length + 1);
|
||||
WriteFully(reinterpret_cast<void*>(base), size);
|
||||
}
|
||||
WriteFully(&ev, sizeof(ev));
|
||||
WriteFully(buffer, name_length + 1);
|
||||
WriteFully(reinterpret_cast<void*>(base), size);
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -246,6 +254,19 @@ class JitDumpCodeObserver : public CodeObserver {
|
|||
uint64_t code_id;
|
||||
};
|
||||
|
||||
struct DebugInfoEvent : BaseEvent {
|
||||
uint64_t address;
|
||||
uint64_t entry_count;
|
||||
// DebugInfoEntry entries[entry_count_];
|
||||
};
|
||||
|
||||
struct DebugInfoEntry {
|
||||
uint64_t address;
|
||||
int32_t line_number;
|
||||
int32_t column;
|
||||
// Followed by nul-terminated name.
|
||||
};
|
||||
|
||||
// ELF machine architectures
|
||||
// From linux/include/uapi/linux/elf-em.h
|
||||
static const uint32_t EM_386 = 3;
|
||||
|
@ -268,6 +289,74 @@ class JitDumpCodeObserver : public CodeObserver {
|
|||
#endif
|
||||
}
|
||||
|
||||
#if ARCH_IS_64_BIT
|
||||
static const int kElfHeaderSize = 0x40;
|
||||
#else
|
||||
static const int kElfHeaderSize = 0x34;
|
||||
#endif
|
||||
|
||||
void WriteDebugInfo(uword base, const CodeComments* comments) {
|
||||
if (comments == nullptr || comments->Length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Open the comments file for the given code object.
|
||||
// Note: for some reason we can't emit all comments into a single file
|
||||
// the mapping between PCs and lines goes out of sync (might be
|
||||
// perf-annotate bug).
|
||||
char* comments_file_name =
|
||||
OS::SCreate(nullptr, "/tmp/jit-%" Pd "-%" Pd ".cmts", pid_, code_id_);
|
||||
const intptr_t filename_length = strlen(comments_file_name);
|
||||
FILE* comments_file = fopen(comments_file_name, "w");
|
||||
setvbuf(comments_file, nullptr, _IOFBF, 2 * MB);
|
||||
|
||||
// Count the number of DebugInfoEntry we are going to emit: one
|
||||
// per PC.
|
||||
intptr_t entry_count = 0;
|
||||
for (uint64_t i = 0, len = comments->Length(); i < len;) {
|
||||
const intptr_t pc_offset = comments->PCOffsetAt(i);
|
||||
while (i < len && comments->PCOffsetAt(i) == pc_offset) {
|
||||
i++;
|
||||
}
|
||||
entry_count++;
|
||||
}
|
||||
|
||||
DebugInfoEvent info;
|
||||
info.event = BaseEvent::kDebugInfo;
|
||||
info.time_stamp = OS::GetCurrentMonotonicTicks();
|
||||
info.address = base;
|
||||
info.entry_count = entry_count;
|
||||
info.size = sizeof(info) +
|
||||
entry_count * (sizeof(DebugInfoEntry) + filename_length + 1);
|
||||
const int32_t padding = Utils::RoundUp(info.size, 8) - info.size;
|
||||
info.size += padding;
|
||||
|
||||
// Write out DebugInfoEvent record followed by entry_count DebugInfoEntry
|
||||
// records.
|
||||
WriteFully(&info, sizeof(info));
|
||||
intptr_t line_number = 0; // Line number within comments_file.
|
||||
for (intptr_t i = 0, len = comments->Length(); i < len;) {
|
||||
const intptr_t pc_offset = comments->PCOffsetAt(i);
|
||||
while (i < len && comments->PCOffsetAt(i) == pc_offset) {
|
||||
line_number += WriteLn(comments_file, comments->CommentAt(i));
|
||||
i++;
|
||||
}
|
||||
DebugInfoEntry entry;
|
||||
entry.address = base + pc_offset + kElfHeaderSize;
|
||||
entry.line_number = line_number;
|
||||
entry.column = 0;
|
||||
WriteFully(&entry, sizeof(entry));
|
||||
WriteFully(comments_file_name, filename_length + 1);
|
||||
}
|
||||
|
||||
// Write out the padding.
|
||||
const char padding_bytes[8] = {0};
|
||||
WriteFully(padding_bytes, padding);
|
||||
|
||||
fclose(comments_file);
|
||||
free(comments_file_name);
|
||||
}
|
||||
|
||||
void WriteHeader() {
|
||||
Header header;
|
||||
header.elf_mach_target = GetElfMachineArchitecture();
|
||||
|
@ -276,8 +365,20 @@ class JitDumpCodeObserver : public CodeObserver {
|
|||
WriteFully(&header, sizeof(header));
|
||||
}
|
||||
|
||||
void WriteFully(void* buffer, size_t size) {
|
||||
const char* ptr = static_cast<char*>(buffer);
|
||||
// Returns number of new-lines written.
|
||||
intptr_t WriteLn(FILE* f, const char* comment) {
|
||||
fputs(comment, f);
|
||||
fputc('\n', f);
|
||||
|
||||
intptr_t line_count = 1;
|
||||
while ((comment = strstr(comment, "\n")) != nullptr) {
|
||||
line_count++;
|
||||
}
|
||||
return line_count;
|
||||
}
|
||||
|
||||
void WriteFully(const void* buffer, size_t size) {
|
||||
const char* ptr = static_cast<const char*>(buffer);
|
||||
while (size > 0) {
|
||||
const size_t written = fwrite(ptr, 1, size, out_file_);
|
||||
if (written == 0) {
|
||||
|
@ -289,11 +390,13 @@ class JitDumpCodeObserver : public CodeObserver {
|
|||
}
|
||||
}
|
||||
|
||||
FILE* out_file_;
|
||||
void* mapped_;
|
||||
long mapped_size_; // NOLINT(runtime/int)
|
||||
const intptr_t pid_;
|
||||
|
||||
intptr_t code_id_;
|
||||
FILE* out_file_ = nullptr;
|
||||
void* mapped_ = nullptr;
|
||||
long mapped_size_ = 0; // NOLINT(runtime/int)
|
||||
|
||||
intptr_t code_id_ = 0;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(JitDumpCodeObserver);
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue