Index: runtime/vm/os_linux.cc |
diff --git a/runtime/vm/os_linux.cc b/runtime/vm/os_linux.cc |
index c7bfcbcce6f185f1725687776eef9616b7109715..91afe1c9757892213648df250af11beff8a90daf 100644 |
--- a/runtime/vm/os_linux.cc |
+++ b/runtime/vm/os_linux.cc |
@@ -14,10 +14,170 @@ |
#include <unistd.h> |
#include "platform/utils.h" |
+#include "vm/code_observers.h" |
+#include "vm/dart.h" |
+#include "vm/debuginfo.h" |
#include "vm/isolate.h" |
+#include "vm/zone.h" |
+ |
namespace dart { |
+// Linux CodeObservers. |
+ |
+DEFINE_FLAG(bool, generate_gdb_symbols, false, |
+ "Generate symbols of generated dart functions for debugging with GDB"); |
+DEFINE_FLAG(bool, generate_perf_events_symbols, false, |
+ "Generate events symbols for profiling with perf"); |
+DEFINE_FLAG(charp, generate_pprof_symbols, false, |
+ "Generate events symbols for profiling with pprof"); |
+ |
+class PerfCodeObserver : public CodeObserver { |
+ public: |
+ PerfCodeObserver() { |
+ Dart_FileOpenCallback file_open = Isolate::file_open_callback(); |
+ if (file_open == NULL) { |
+ return; |
+ } |
+ const char* format = "/tmp/perf-%ld.map"; |
+ intptr_t pid = getpid(); |
+ intptr_t len = OS::SNPrint(NULL, 0, format, pid); |
+ char* filename = new char[len + 1]; |
+ OS::SNPrint(filename, len + 1, format, pid); |
+ out_file_ = (*file_open)(filename); |
+ } |
+ |
+ ~PerfCodeObserver() { |
+ Dart_FileCloseCallback file_close = Isolate::file_close_callback(); |
+ if (file_close == NULL) { |
+ return; |
+ } |
+ ASSERT(out_file_ != NULL); |
+ (*file_close)(out_file_); |
+ } |
+ |
+ virtual bool IsActive() const { |
+ return FLAG_generate_perf_events_symbols; |
+ } |
+ |
+ virtual void Notify(const char* name, |
+ uword base, |
+ uword prologue_offset, |
+ uword size, |
+ bool optimized) { |
+ Dart_FileWriteCallback file_write = Isolate::file_write_callback(); |
+ ASSERT(file_write != NULL); |
+ const char* format = "%"Px" %"Px" %s%s\n"; |
+ const char* marker = optimized ? "*" : ""; |
+ intptr_t len = OS::SNPrint(NULL, 0, format, base, size, marker, name); |
+ char* buffer = Isolate::Current()->current_zone()->Alloc<char>(len + 1); |
+ OS::SNPrint(buffer, len + 1, format, base, size, marker, name); |
+ ASSERT(out_file_ != NULL); |
+ (*file_write)(buffer, len, out_file_); |
+ } |
+ |
+ private: |
+ void* out_file_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(PerfCodeObserver); |
+}; |
+ |
+class PprofCodeObserver : public CodeObserver { |
+ public: |
+ PprofCodeObserver() { |
+ pprof_symbol_generator_ = DebugInfo::NewGenerator(); |
+ } |
+ |
+ ~PprofCodeObserver() { |
+ Dart_FileOpenCallback file_open = Isolate::file_open_callback(); |
+ if (file_open == NULL) { |
+ return; |
+ } |
+ Dart_FileCloseCallback file_close = Isolate::file_close_callback(); |
+ if (file_close == NULL) { |
+ return; |
+ } |
+ Dart_FileWriteCallback file_write = Isolate::file_write_callback(); |
+ if (file_write == NULL) { |
+ return; |
+ } |
+ if (FLAG_generate_pprof_symbols == NULL) { |
+ return; |
+ } |
+ const char* filename = FLAG_generate_pprof_symbols; |
+ void* out_file = (*file_open)(filename); |
+ ASSERT(out_file != NULL); |
+ DebugInfo::ByteBuffer* debug_region = new DebugInfo::ByteBuffer(); |
+ ASSERT(debug_region != NULL); |
+ pprof_symbol_generator_->WriteToMemory(debug_region); |
+ int buffer_size = debug_region->size(); |
+ void* buffer = debug_region->data(); |
+ delete debug_region; |
+ if (buffer_size > 0) { |
+ ASSERT(buffer != NULL); |
+ (*file_write)(buffer, buffer_size, out_file); |
+ } |
+ (*file_close)(out_file); |
+ DebugInfo::UnregisterAllSections(); |
+ } |
+ |
+ virtual bool IsActive() const { |
+ return FLAG_generate_pprof_symbols != NULL; |
+ } |
+ |
+ virtual void Notify(const char* name, |
+ uword base, |
+ uword prologue_offset, |
+ uword size, |
+ bool optimized) { |
+ ASSERT(pprof_symbol_generator_ != NULL); |
+ pprof_symbol_generator_->AddCode(base, size); |
+ pprof_symbol_generator_->AddCodeRegion(name, base, size); |
+ } |
+ |
+ private: |
+ DebugInfo* pprof_symbol_generator_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(PprofCodeObserver); |
+}; |
+ |
+class GdbCodeObserver : public CodeObserver { |
+ public: |
+ GdbCodeObserver() { } |
+ |
+ virtual bool IsActive() const { |
+ return FLAG_generate_gdb_symbols; |
+ } |
+ |
+ virtual void Notify(const char* name, |
+ uword base, |
+ uword prologue_offset, |
+ uword size, |
+ bool optimized) { |
+ if (prologue_offset > 0) { |
+ // In order to ensure that gdb sees the first instruction of a function |
+ // as the prologue sequence we register two symbols for the cases when |
+ // the prologue sequence is not the first instruction: |
+ // <name>_entry is used for code preceding the prologue sequence. |
+ // <name> for rest of the code (first instruction is prologue sequence). |
+ const char* kFormat = "%s_%s"; |
+ intptr_t len = OS::SNPrint(NULL, 0, kFormat, name, "entry"); |
+ char* pname = Isolate::Current()->current_zone()->Alloc<char>(len + 1); |
+ OS::SNPrint(pname, (len + 1), kFormat, name, "entry"); |
+ DebugInfo::RegisterSection(pname, base, size); |
+ DebugInfo::RegisterSection(name, |
+ (base + prologue_offset), |
+ (size - prologue_offset)); |
+ } else { |
+ DebugInfo::RegisterSection(name, base, size); |
+ } |
+ } |
+ |
+ private: |
+ DISALLOW_COPY_AND_ASSIGN(GdbCodeObserver); |
+}; |
+ |
+ |
static bool LocalTime(int64_t seconds_since_epoch, tm* tm_result) { |
time_t seconds = static_cast<time_t>(seconds_since_epoch); |
if (seconds != seconds_since_epoch) return false; |
@@ -199,6 +359,22 @@ bool OS::StringToInt64(const char* str, int64_t* value) { |
} |
+void OS::RegisterCodeObservers() { |
+ if (FLAG_generate_perf_events_symbols) { |
+ CodeObservers::Register(new PerfCodeObserver); |
+ } |
+ if (FLAG_generate_gdb_symbols) { |
+ CodeObservers::Register(new GdbCodeObserver); |
+ } |
+ if (FLAG_generate_pprof_symbols != NULL) { |
+ CodeObservers::Register(new PprofCodeObserver); |
+ } |
+#if defined(DART_VTUNE_SUPPORT) |
+ Register(new VTuneCodeObserver); |
+#endif |
+} |
+ |
+ |
void OS::PrintErr(const char* format, ...) { |
va_list args; |
va_start(args, format); |