Index: runtime/vm/profiler.cc |
diff --git a/runtime/vm/profiler.cc b/runtime/vm/profiler.cc |
index d2545208bf45d2c164446119a59f562de67be46a..51eb1769f7a4a09cb2474058fa3ef458046e28c9 100644 |
--- a/runtime/vm/profiler.cc |
+++ b/runtime/vm/profiler.cc |
@@ -6,7 +6,9 @@ |
#include "platform/utils.h" |
+#include "vm/allocation.h" |
#include "vm/atomic.h" |
+#include "vm/code_patcher.h" |
#include "vm/isolate.h" |
#include "vm/json_stream.h" |
#include "vm/native_symbol.h" |
@@ -218,124 +220,304 @@ void Profiler::RecordSampleInterruptCallback( |
} |
-void Profiler::PrintToJSONStream(Isolate* isolate, JSONStream* stream) { |
- ASSERT(isolate == Isolate::Current()); |
- UNIMPLEMENTED(); |
-} |
+class SymbolLookupCache { |
+ public: |
+ explicit SymbolLookupCache(const intptr_t cache_size = 32) { |
+ cache_index_ = 0; |
+ cache_size_ = cache_size; |
+ pcs_ = reinterpret_cast<uintptr_t*>( |
+ calloc(cache_size, sizeof(uintptr_t))); // NOLINT |
+ names_ = reinterpret_cast<const char**>( |
+ calloc(cache_size, sizeof(const char*))); // NOLINT |
+ } |
+ |
+ virtual ~SymbolLookupCache() { |
+ for (intptr_t i = 0; i < cache_size_; i++) { |
+ ClearCacheIndex(i); |
+ } |
+ free(pcs_); |
+ free(names_); |
+ } |
+ |
+ const char* Lookup(uintptr_t pc) { |
+ const char* symbol = LookupInCache(pc); |
+ if (symbol != NULL) { |
+ return symbol; |
+ } |
+ symbol = QuerySource(pc); |
+ if (symbol != NULL) { |
+ InsertIntoCache(pc, symbol); |
+ } |
+ return symbol; |
+ } |
+ |
+ virtual const char* QuerySource(uintptr_t pc) const = 0; |
+ protected: |
+ const char* GenerateSymbolName(const char* prefix, uintptr_t pc) const { |
+ // Generate a name by binning into buckets by PC. |
+ const intptr_t kBucketSize = 256; |
+ const intptr_t kBucketMask = ~(kBucketSize - 1); |
+ pc &= kBucketMask; |
+ const intptr_t kBuffSize = 256; |
+ char buff[kBuffSize]; |
+ OS::SNPrint(&buff[0], kBuffSize-1, "%s [%" Px ", %" Px ")", |
+ prefix, pc, pc + kBucketSize); |
+ return strdup(buff); |
+ } |
-static const char* FindSymbolName(uintptr_t pc, bool* symbol_name_allocated) { |
- // TODO(johnmccutchan): Differentiate between symbols which can't be found |
- // and symbols which were GCed. (Heap::CodeContains). |
- ASSERT(symbol_name_allocated != NULL); |
- const char* symbol_name = "Unknown"; |
- *symbol_name_allocated = false; |
- if (pc == 0) { |
- return const_cast<char*>(Sample::kNoFrame); |
- } |
- const Code& code = Code::Handle(Code::LookupCode(pc)); |
- if (!code.IsNull()) { |
- const Function& function = Function::Handle(code.function()); |
- if (!function.IsNull()) { |
- const String& name = String::Handle(function.QualifiedUserVisibleName()); |
- if (!name.IsNull()) { |
- symbol_name = name.ToCString(); |
- return symbol_name; |
+ private: |
+ const char* LookupInCache(uintptr_t pc) { |
+ for (intptr_t i = 0; i < cache_size_; i++) { |
+ if (pcs_[i] == pc) { |
+ ASSERT(names_[i] != NULL); |
+ return names_[i]; |
} |
} |
- } else { |
- // Possibly a native symbol. |
+ return NULL; |
+ } |
+ |
+ intptr_t GetCacheIndex(uintptr_t pc) { |
+ cache_index_ = (cache_index_ + 1) % cache_size_; |
+ return cache_index_; |
+ } |
+ |
+ void InsertIntoCache(uintptr_t pc, const char* name) { |
+ intptr_t index = GetCacheIndex(pc); |
+ ClearCacheIndex(index); |
+ pcs_[index] = pc; |
+ names_[index] = name; |
+ } |
+ |
+ void ClearCacheIndex(intptr_t index) { |
+ free(const_cast<char*>(names_[index])); |
+ names_[index] = NULL; |
+ pcs_[index] = 0; |
+ } |
+ |
+ intptr_t cache_index_; |
+ intptr_t cache_size_; |
+ uintptr_t* pcs_; |
+ const char** names_; |
+}; |
+ |
+ |
+class NativeSymbolLookup : public SymbolLookupCache { |
+ public: |
+ NativeSymbolLookup() : SymbolLookupCache(32) { |
+ } |
+ |
+ const char* QuerySource(uintptr_t pc) const { |
char* native_name = NativeSymbolResolver::LookupSymbolName(pc); |
- if (native_name != NULL) { |
- symbol_name = native_name; |
- *symbol_name_allocated = true; |
- return symbol_name; |
+ if (native_name == NULL) { |
+ return GenerateSymbolName("Unknown", pc); |
} |
+ return native_name; |
} |
- const intptr_t kBucketSize = 256; |
- const intptr_t kBucketMask = ~(kBucketSize - 1); |
- // Not a Dart symbol or a native symbol. Bin into buckets by PC. |
- pc &= kBucketMask; |
+}; |
+ |
+ |
+class DartSymbolLookup : public SymbolLookupCache { |
+ public: |
+ explicit DartSymbolLookup(Isolate* isolate) : SymbolLookupCache(32), |
+ isolate_(isolate), |
+ heap_(isolate_->heap()) { |
+ } |
+ |
+ bool InDartCodeSpace(uintptr_t pc) { |
+ ASSERT(heap_ != NULL); |
+ return heap_->CodeContains(pc); |
+ } |
+ |
+ const char* QuerySource(uintptr_t pc) const { |
+ const Code& code = Code::Handle(isolate_, Code::LookupCode(pc)); |
+ if (!code.IsNull()) { |
+ const Function& function = Function::Handle(isolate_, code.function()); |
+ if (!function.IsNull()) { |
+ const String& name = |
+ String::Handle(isolate_, function.QualifiedUserVisibleName()); |
+ if (!name.IsNull()) { |
+ return strdup(name.ToCString()); |
+ } |
+ } else { |
+ return GenerateSymbolName("Detached", pc); |
+ } |
+ } |
+ return GenerateSymbolName("Collected", pc); |
+ } |
+ |
+ private: |
+ Isolate* isolate_; |
+ Heap* heap_; |
+}; |
+ |
+ |
+class DartCodeTable { |
+ public: |
+ explicit DartCodeTable(Isolate* isolate) : |
+ heap_(isolate->heap()), |
+ code_table_(GrowableObjectArray::Handle(isolate)) { |
+ code_table_ ^= GrowableObjectArray::New(); |
+ ASSERT(!code_table_.IsNull()); |
+ } |
+ |
+ ~DartCodeTable() { |
+ } |
+ |
+ bool Contains(const Code& code) { |
+ intptr_t length = code_table_.Length(); |
+ Code& table_code = Code::Handle(); |
+ for (intptr_t i = 0; i < length; i++) { |
+ table_code ^= code_table_.At(i); |
+ if (table_code.IsNull()) { |
+ return false; |
+ } |
+ ASSERT(!table_code.IsNull()); |
+ if (table_code.raw() == code.raw()) { |
+ return true; |
+ } |
+ } |
+ return false; |
+ } |
+ |
+ void Add(const Code& code) { |
+ ASSERT(!Contains(code)); |
+ code_table_.Add(code); |
+ } |
+ |
+ private: |
+ Heap* heap_; |
+ GrowableObjectArray& code_table_; |
+}; |
+ |
+ |
+class ProfilerSymbolHelper { |
+ public: |
+ explicit ProfilerSymbolHelper(Isolate* isolate) : |
+ dart_symbol_(isolate), |
+ code_table_(isolate) { |
+ } |
+ |
+ ~ProfilerSymbolHelper() { |
+ } |
+ |
+ |
+ const char* GetSymbol(uintptr_t pc) { |
+ if (pc == 0) { |
+ return "<no frame>"; |
+ } |
+ if (dart_symbol_.InDartCodeSpace(pc)) { |
Ivan Posva
2013/12/30 23:06:09
Restructuring this as discussed will avoid iterati
|
+ return dart_symbol_.Lookup(pc); |
+ } else { |
+ return native_symbol_.Lookup(pc); |
+ } |
+ } |
+ |
+ |
+ RawCode* GetNewCode(uintptr_t pc) { |
+ Code& new_code = Code::Handle(); |
+ new_code ^= Code::LookupCode(pc); |
Ivan Posva
2013/12/30 23:06:09
Here you are walking through the heap again. Looki
|
+ if (!new_code.IsNull()) { |
+ Function& function = Function::Handle(new_code.function()); |
+ if (function.IsNull()) { |
+ // If code no longer has a function, ignore it. |
+ new_code ^= Code::null(); |
+ } else if (code_table_.Contains(new_code)) { |
+ // If code has been serialized, ignore it. |
+ new_code ^= Code::null(); |
+ } else { |
+ // Add code to code table. |
+ code_table_.Add(new_code); |
+ } |
+ } |
+ return new_code.raw(); |
+ } |
+ |
+ private: |
+ DartSymbolLookup dart_symbol_; |
+ DartCodeTable code_table_; |
+ NativeSymbolLookup native_symbol_; |
+}; |
+ |
+ |
+void Profiler::PrintToJSONStream(Isolate* isolate, JSONStream* stream) { |
+ ASSERT(isolate == Isolate::Current()); |
+ // Disable profile interrupts while processing the buffer. |
+ EndExecution(isolate); |
+ MutexLocker profiler_data_lock(isolate->profiler_data_mutex()); |
+ IsolateProfilerData* profiler_data = isolate->profiler_data(); |
+ if (profiler_data == NULL) { |
+ return; |
+ } |
+ SampleBuffer* sample_buffer = profiler_data->sample_buffer(); |
+ ASSERT(sample_buffer != NULL); |
+ ProfilerSymbolHelper symbol_helper(isolate); |
{ |
- const intptr_t kBuffSize = 256; |
- char buff[kBuffSize]; |
- OS::SNPrint(&buff[0], kBuffSize-1, "Unknown [%" Px ", %" Px ")", |
- pc, pc + kBucketSize); |
- symbol_name = strdup(buff); |
- *symbol_name_allocated = true; |
+ JSONArray events(stream); |
+ for (intptr_t i = 0; i < sample_buffer->capacity(); i++) { |
+ Sample sample = sample_buffer->GetSample(i); |
+ if (sample.isolate != isolate) { |
+ continue; |
+ } |
+ if (sample.timestamp == 0) { |
+ continue; |
+ } |
+ WriteSample(isolate, &symbol_helper, &sample, events); |
+ } |
} |
- return symbol_name; |
+ // Enable profile interrupts. |
+ BeginExecution(isolate); |
} |
-void Profiler::WriteTracingSample(Isolate* isolate, intptr_t pid, |
- Sample* sample, JSONArray& events) { |
+void Profiler::WriteSample(Isolate* isolate, |
+ ProfilerSymbolHelper* symbol_helper, |
+ Sample* sample, JSONArray& events) { |
+ StackZone zone(isolate); |
+ Code& new_code = Code::Handle(isolate); |
Sample::SampleType type = sample->type; |
- intptr_t tid = Thread::ThreadIdToIntPtr(sample->tid); |
+ if (type != Sample::kIsolateSample) { |
+ return; |
+ } |
double timestamp = static_cast<double>(sample->timestamp); |
- const char* isolate_name = isolate->name(); |
- switch (type) { |
- case Sample::kIsolateStart: { |
- JSONObject begin(&events); |
- begin.AddProperty("ph", "B"); |
- begin.AddProperty("tid", tid); |
- begin.AddProperty("pid", pid); |
- begin.AddProperty("name", isolate_name); |
- begin.AddProperty("ts", timestamp); |
+ bool any = false; |
+ for (int i = Sample::kNumStackFrames - 1; i >= 0; i--) { |
+ if (sample->pcs[i] == 0) { |
+ continue; |
} |
- break; |
- case Sample::kIsolateStop: { |
- JSONObject begin(&events); |
- begin.AddProperty("ph", "E"); |
- begin.AddProperty("tid", tid); |
- begin.AddProperty("pid", pid); |
- begin.AddProperty("name", isolate_name); |
- begin.AddProperty("ts", timestamp); |
- } |
- break; |
- case Sample::kIsolateSample: |
- // Write "B" events. |
+ any = true; |
+ } |
+ if (!any) { |
+ // No frames in this sample. |
+ return; |
+ } |
+ { |
+ JSONObject sample_event(&events); |
+ sample_event.AddProperty("ts", timestamp); |
+ { |
+ JSONArray frames(&sample_event, "f"); |
for (int i = Sample::kNumStackFrames - 1; i >= 0; i--) { |
- bool symbol_name_allocated = false; |
- const char* symbol_name = FindSymbolName(sample->pcs[i], |
- &symbol_name_allocated); |
- { |
- JSONObject begin(&events); |
- begin.AddProperty("ph", "B"); |
- begin.AddProperty("tid", tid); |
- begin.AddProperty("pid", pid); |
- begin.AddProperty("name", symbol_name); |
- begin.AddProperty("ts", timestamp); |
+ if (sample->pcs[i] == 0) { |
+ continue; |
} |
- if (symbol_name_allocated) { |
- free(const_cast<char*>(symbol_name)); |
+ const char* symbol_name = symbol_helper->GetSymbol(sample->pcs[i]); |
+ new_code ^= symbol_helper->GetNewCode(sample->pcs[i]); |
+ if (!new_code.IsNull()) { |
+ // Dump code. |
+ frames.AddValue(new_code, false); |
} |
- } |
- // Write "E" events. |
- for (int i = 0; i < Sample::kNumStackFrames; i++) { |
- bool symbol_name_allocated = false; |
- const char* symbol_name = FindSymbolName(sample->pcs[i], |
- &symbol_name_allocated); |
{ |
- JSONObject begin(&events); |
- begin.AddProperty("ph", "E"); |
- begin.AddProperty("tid", tid); |
- begin.AddProperty("pid", pid); |
- begin.AddProperty("name", symbol_name); |
- begin.AddProperty("ts", timestamp); |
- } |
- if (symbol_name_allocated) { |
- free(const_cast<char*>(symbol_name)); |
+ JSONObject tick(&frames); |
+ tick.AddProperty("s", symbol_name); |
+ tick.AddPropertyF("pc", "%" Px "", sample->pcs[i]); |
} |
} |
- break; |
- default: |
- UNIMPLEMENTED(); |
+ } |
} |
} |
-void Profiler::WriteTracing(Isolate* isolate) { |
+void Profiler::WriteSamples(Isolate* isolate) { |
if (isolate == NULL) { |
return; |
} |
@@ -354,41 +536,10 @@ void Profiler::WriteTracing(Isolate* isolate) { |
return; |
} |
// We will be looking up code objects within the isolate. |
- ASSERT(Isolate::Current() != NULL); |
- // We do not want to be interrupted while processing the buffer. |
- EndExecution(isolate); |
- MutexLocker profiler_data_lock(isolate->profiler_data_mutex()); |
- IsolateProfilerData* profiler_data = isolate->profiler_data(); |
- if (profiler_data == NULL) { |
- return; |
- } |
- SampleBuffer* sample_buffer = profiler_data->sample_buffer(); |
- ASSERT(sample_buffer != NULL); |
+ ASSERT(Isolate::Current() == isolate); |
JSONStream stream(10 * MB); |
intptr_t pid = OS::ProcessId(); |
- { |
- JSONArray events(&stream); |
- { |
- JSONObject process_name(&events); |
- process_name.AddProperty("name", "process_name"); |
- process_name.AddProperty("ph", "M"); |
- process_name.AddProperty("pid", pid); |
- { |
- JSONObject args(&process_name, "args"); |
- args.AddProperty("name", "Dart VM"); |
- } |
- } |
- for (intptr_t i = 0; i < sample_buffer->capacity(); i++) { |
- Sample* sample = sample_buffer->GetSample(i); |
- if (sample->isolate != isolate) { |
- continue; |
- } |
- if (sample->timestamp == 0) { |
- continue; |
- } |
- WriteTracingSample(isolate, pid, sample, events); |
- } |
- } |
+ PrintToJSONStream(isolate, &stream); |
const char* format = "%s/dart-profile-%" Pd "-%" Pd ".json"; |
intptr_t len = OS::SNPrint(NULL, 0, format, |
FLAG_profile_dir, pid, isolate->main_port()); |
@@ -404,7 +555,6 @@ void Profiler::WriteTracing(Isolate* isolate) { |
ASSERT(buffer != NULL); |
file_write(buffer->buf(), buffer->length(), f); |
file_close(f); |
- BeginExecution(isolate); |
} |