Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(93)

Unified Diff: runtime/vm/profiler.cc

Issue 100103011: Changes to support dprof and Observatory profiler UIs (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 12 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « runtime/vm/profiler.h ('k') | runtime/vm/profiler_test.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: runtime/vm/profiler.cc
diff --git a/runtime/vm/profiler.cc b/runtime/vm/profiler.cc
index d2545208bf45d2c164446119a59f562de67be46a..328c78f562ecb9fc951ccaefbc6e5af7e5534b64 100644
--- a/runtime/vm/profiler.cc
+++ b/runtime/vm/profiler.cc
@@ -2,11 +2,11 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-#include <cstdio>
-
#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 +218,395 @@ void Profiler::RecordSampleInterruptCallback(
}
-void Profiler::PrintToJSONStream(Isolate* isolate, JSONStream* stream) {
- ASSERT(isolate == Isolate::Current());
- UNIMPLEMENTED();
-}
+struct AddressEntry {
+ uintptr_t pc;
+ uintptr_t ticks;
+};
+
+
+// A region of code. Each region is a kind of code (Dart, Collected, or Native).
+class CodeRegion : public ZoneAllocated {
+ public:
+ enum Kind {
+ kDartCode,
+ kCollectedCode,
+ kNativeCode
+ };
+
+ CodeRegion(Kind kind, uintptr_t start, uintptr_t end) :
+ kind_(kind),
+ start_(start),
+ end_(end),
+ inclusive_ticks_(0),
+ exclusive_ticks_(0),
+ name_(NULL),
+ address_table_(new ZoneGrowableArray<AddressEntry>()) {
+ }
+
+ ~CodeRegion() {
+ }
+
+ uintptr_t start() const { return start_; }
+ void set_start(uintptr_t start) {
+ start_ = start;
+ }
+
+ uintptr_t end() const { return end_; }
+ void set_end(uintptr_t end) {
+ end_ = end;
+ }
+ void AdjustExtent(uintptr_t start, uintptr_t end) {
+ if (start < start_) {
+ start_ = start;
+ }
+ if (end > end_) {
+ end_ = end;
+ }
+ }
-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;
+ bool contains(uintptr_t pc) const {
+ return (pc >= start_) && (pc < end_);
+ }
+
+ intptr_t inclusive_ticks() const { return inclusive_ticks_; }
+ void set_inclusive_ticks(intptr_t inclusive_ticks) {
+ inclusive_ticks_ = inclusive_ticks;
+ }
+
+ intptr_t exclusive_ticks() const { return exclusive_ticks_; }
+ void set_exclusive_ticks(intptr_t exclusive_ticks) {
+ exclusive_ticks_ = exclusive_ticks;
+ }
+
+ const char* name() const { return name_; }
+ void SetName(const char* name) {
+ if (name == NULL) {
+ name_ = NULL;
+ }
+ intptr_t len = strlen(name);
+ name_ = Isolate::Current()->current_zone()->Alloc<const char>(len + 1);
+ strncpy(const_cast<char*>(name_), name, len);
+ const_cast<char*>(name_)[len] = '\0';
+ }
+
+ Kind kind() const { return kind_; }
+
+ static const char* KindToCString(Kind kind) {
+ switch (kind) {
+ case kDartCode:
+ return "Dart";
+ case kCollectedCode:
+ return "Collected";
+ case kNativeCode:
+ return "Native";
+ }
+ UNREACHABLE();
+ return NULL;
+ }
+
+ void AddTick(bool exclusive) {
+ if (exclusive) {
+ exclusive_ticks_++;
+ } else {
+ inclusive_ticks_++;
+ }
+ }
+
+ void AddTickAtAddress(uintptr_t pc) {
+ const intptr_t length = address_table_->length();
+ intptr_t i = 0;
+ for (; i < length; i++) {
+ AddressEntry& entry = (*address_table_)[i];
+ if (entry.pc == pc) {
+ entry.ticks++;
+ return;
+ }
+ if (entry.pc > pc) {
+ break;
}
}
- } else {
- // Possibly a native symbol.
- char* native_name = NativeSymbolResolver::LookupSymbolName(pc);
- if (native_name != NULL) {
- symbol_name = native_name;
- *symbol_name_allocated = true;
- return symbol_name;
+ AddressEntry entry;
+ entry.pc = pc;
+ entry.ticks = 1;
+ if (i < length) {
+ // Insert at i.
+ address_table_->InsertAt(i, entry);
+ } else {
+ // Add to end.
+ address_table_->Add(entry);
}
}
- 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;
- {
- const intptr_t kBuffSize = 256;
+
+
+ void PrintToJSONArray(JSONArray* events, bool full) {
+ JSONObject obj(events);
+ obj.AddProperty("type", "ProfileCode");
+ obj.AddProperty("kind", KindToCString(kind()));
+ obj.AddPropertyF("inclusive_ticks", "%" Pd "", inclusive_ticks());
+ obj.AddPropertyF("exclusive_ticks", "%" Pd "", exclusive_ticks());
+ if (kind() == kDartCode) {
+ // Look up code in Dart heap.
+ Code& code = Code::Handle(Code::LookupCode(start()));
+ Function& func = Function::Handle();
+ ASSERT(!code.IsNull());
+ func ^= code.function();
+ if (func.IsNull()) {
+ if (name() == NULL) {
+ GenerateAndSetSymbolName("Stub");
+ }
+ obj.AddPropertyF("start", "%" Px "", start());
+ obj.AddPropertyF("end", "%" Px "", end());
+ obj.AddProperty("name", name());
+ } else {
+ obj.AddProperty("code", code, !full);
+ }
+ } else if (kind() == kCollectedCode) {
+ if (name() == NULL) {
+ GenerateAndSetSymbolName("Collected");
+ }
+ obj.AddPropertyF("start", "%" Px "", start());
+ obj.AddPropertyF("end", "%" Px "", end());
+ obj.AddProperty("name", name());
+ } else {
+ ASSERT(kind() == kNativeCode);
+ if (name() == NULL) {
+ GenerateAndSetSymbolName("Native");
+ }
+ obj.AddPropertyF("start", "%" Px "", start());
+ obj.AddPropertyF("end", "%" Px "", end());
+ obj.AddProperty("name", name());
+ }
+ {
+ JSONArray ticks(&obj, "ticks");
+ for (intptr_t i = 0; i < address_table_->length(); i++) {
+ const AddressEntry& entry = (*address_table_)[i];
+ ticks.AddValueF("%" Px "", entry.pc);
+ ticks.AddValueF("%" Pd "", entry.ticks);
+ }
+ }
+ }
+
+ private:
+ void GenerateAndSetSymbolName(const char* prefix) {
+ const intptr_t kBuffSize = 512;
char buff[kBuffSize];
- OS::SNPrint(&buff[0], kBuffSize-1, "Unknown [%" Px ", %" Px ")",
- pc, pc + kBucketSize);
- symbol_name = strdup(buff);
- *symbol_name_allocated = true;
+ OS::SNPrint(&buff[0], kBuffSize-1, "%s [%" Px ", %" Px ")",
+ prefix, start(), end());
+ SetName(buff);
}
- return symbol_name;
-}
+ Kind kind_;
+ uintptr_t start_;
+ uintptr_t end_;
+ intptr_t inclusive_ticks_;
+ intptr_t exclusive_ticks_;
+ const char* name_;
+ ZoneGrowableArray<AddressEntry>* address_table_;
-void Profiler::WriteTracingSample(Isolate* isolate, intptr_t pid,
- Sample* sample, JSONArray& events) {
- Sample::SampleType type = sample->type;
- intptr_t tid = Thread::ThreadIdToIntPtr(sample->tid);
- 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);
+ DISALLOW_COPY_AND_ASSIGN(CodeRegion);
+};
+
+
+// All code regions. Code region tables are built on demand when a profile
+// is requested (through the service or on isolate shutdown).
+class ProfilerCodeRegionTable : public ValueObject {
+ public:
+ explicit ProfilerCodeRegionTable(Isolate* isolate) :
+ heap_(isolate->heap()),
+ code_region_table_(new ZoneGrowableArray<CodeRegion*>(64)) {
+ }
+
+ ~ProfilerCodeRegionTable() {
+ }
+
+ void AddTick(uintptr_t pc, bool exclusive, bool tick_address) {
+ intptr_t index = FindIndex(pc);
+ if (index < 0) {
+ CodeRegion* code_region = CreateCodeRegion(pc);
+ ASSERT(code_region != NULL);
+ index = InsertCodeRegion(code_region);
}
- 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);
+ ASSERT(index >= 0);
+ ASSERT(index < code_region_table_->length());
+ (*code_region_table_)[index]->AddTick(exclusive);
+ if (tick_address) {
+ (*code_region_table_)[index]->AddTickAtAddress(pc);
}
- break;
- case Sample::kIsolateSample:
- // Write "B" events.
- 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 (symbol_name_allocated) {
- free(const_cast<char*>(symbol_name));
- }
+ }
+
+ intptr_t Length() const { return code_region_table_->length(); }
+
+ CodeRegion* At(intptr_t idx) {
+ return (*code_region_table_)[idx];
+ }
+
+ private:
+ intptr_t FindIndex(uintptr_t pc) {
+ const intptr_t length = code_region_table_->length();
+ for (intptr_t i = 0; i < length; i++) {
+ const CodeRegion* code_region = (*code_region_table_)[i];
+ if (code_region->contains(pc)) {
+ return i;
}
- // 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));
+ }
+ return -1;
+ }
+
+ CodeRegion* CreateCodeRegion(uintptr_t pc) {
+ Code& code = Code::Handle(Code::LookupCode(pc));
+ if (!code.IsNull()) {
+ return new CodeRegion(CodeRegion::kDartCode, code.EntryPoint(),
+ code.EntryPoint() + code.Size());
+ }
+ if (heap_->CodeContains(pc)) {
+ const intptr_t kDartCodeAlignment = 0x10;
+ const intptr_t kDartCodeAlignmentMask = ~(kDartCodeAlignment - 1);
+ return new CodeRegion(CodeRegion::kCollectedCode,
+ (pc & kDartCodeAlignmentMask),
+ (pc & kDartCodeAlignmentMask) + kDartCodeAlignment);
+ }
+ uintptr_t native_start = 0;
+ char* native_name = NativeSymbolResolver::LookupSymbolName(pc,
+ &native_start);
+ if (native_name == NULL) {
+ return new CodeRegion(CodeRegion::kNativeCode, pc, pc + 1);
+ }
+ ASSERT(pc >= native_start);
+ CodeRegion* code_region =
+ new CodeRegion(CodeRegion::kNativeCode, native_start, pc + 1);
+ code_region->SetName(native_name);
+ free(native_name);
+ return code_region;
+ }
+
+ intptr_t InsertCodeRegion(CodeRegion* code_region) {
+ const intptr_t length = code_region_table_->length();
+ const uintptr_t start = code_region->start();
+ const uintptr_t end = code_region->end();
+ intptr_t i = 0;
+ for (; i < length; i++) {
+ CodeRegion* region = (*code_region_table_)[i];
+ if (region->contains(start) || region->contains(end - 1)) {
+ // We should only see overlapping native code regions.
+ ASSERT(region->kind() == CodeRegion::kNativeCode);
+ // When code regions overlap, they should be of the same kind.
+ ASSERT(region->kind() == code_region->kind());
+ // Overlapping code region.
+ region->AdjustExtent(start, end);
+ return i;
+ } else if (start >= region->end()) {
+ // Insert here.
+ break;
+ }
+ }
+ if (i != length) {
+ code_region_table_->InsertAt(i, code_region);
+ return i;
+ }
+ code_region_table_->Add(code_region);
+ return code_region_table_->length() - 1;
+ }
+
+ Heap* heap_;
+ ZoneGrowableArray<CodeRegion*>* code_region_table_;
+};
+
+
+void Profiler::PrintToJSONStream(Isolate* isolate, JSONStream* stream,
+ bool full) {
+ 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) {
+ JSONObject error(stream);
+ error.AddProperty("type", "Error");
+ error.AddProperty("text", "Isolate does not have profiling enabled.");
+ return;
+ }
+ SampleBuffer* sample_buffer = profiler_data->sample_buffer();
+ ASSERT(sample_buffer != NULL);
+ {
+ StackZone zone(isolate);
+ {
+ // Build code region table.
+ ProfilerCodeRegionTable code_region_table(isolate);
+ intptr_t samples =
+ ProcessSamples(isolate, &code_region_table, sample_buffer);
+ {
+ // Serialize to JSON.
+ JSONObject obj(stream);
+ obj.AddProperty("type", "Profile");
+ obj.AddProperty("samples", samples);
+ JSONArray codes(&obj, "codes");
+ for (intptr_t i = 0; i < code_region_table.Length(); i++) {
+ CodeRegion* region = code_region_table.At(i);
+ ASSERT(region != NULL);
+ region->PrintToJSONArray(&codes, full);
}
}
- break;
- default:
- UNIMPLEMENTED();
+ }
+ }
+ // Enable profile interrupts.
+ BeginExecution(isolate);
+}
+
+
+intptr_t Profiler::ProcessSamples(Isolate* isolate,
+ ProfilerCodeRegionTable* code_region_table,
+ SampleBuffer* sample_buffer) {
+ int64_t start = OS::GetCurrentTimeMillis();
+ intptr_t samples = 0;
+ 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;
+ }
+ samples += ProcessSample(isolate, code_region_table, &sample);
+ }
+ int64_t end = OS::GetCurrentTimeMillis();
+ if (FLAG_trace_profiled_isolates) {
+ int64_t delta = end - start;
+ OS::Print("Processed %" Pd " samples from %s in %" Pd64 " milliseconds.\n",
+ samples,
+ isolate->name(),
+ delta);
}
+ return samples;
}
-void Profiler::WriteTracing(Isolate* isolate) {
+intptr_t Profiler::ProcessSample(Isolate* isolate,
+ ProfilerCodeRegionTable* code_region_table,
+ Sample* sample) {
+ Sample::SampleType type = sample->type;
+ if (type != Sample::kIsolateSample) {
+ return 0;
+ }
+ if (sample->pcs[0] == 0) {
+ // No frames in this sample.
+ return 0;
+ }
+ intptr_t i = 0;
+ // i points to the leaf (exclusive) PC sample. Do not tick the address.
+ code_region_table->AddTick(sample->pcs[i], true, false);
+ // Give all frames an inclusive tick and tick the address.
+ for (; i < Sample::kNumStackFrames; i++) {
+ if (sample->pcs[i] == 0) {
+ break;
+ }
+ code_region_table->AddTick(sample->pcs[i], false, true);
+ }
+ return 1;
+}
+
+
+void Profiler::WriteProfile(Isolate* isolate) {
if (isolate == NULL) {
return;
}
@@ -354,41 +625,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, true);
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 +644,6 @@ void Profiler::WriteTracing(Isolate* isolate) {
ASSERT(buffer != NULL);
file_write(buffer->buf(), buffer->length(), f);
file_close(f);
- BeginExecution(isolate);
}
@@ -424,10 +663,6 @@ IsolateProfilerData::~IsolateProfilerData() {
}
-const char* Sample::kLookupSymbol = "Symbol Not Looked Up";
-const char* Sample::kNoSymbol = "No Symbol Found";
-const char* Sample::kNoFrame = "<no frame>";
-
void Sample::Init(SampleType type, Isolate* isolate, int64_t timestamp,
ThreadId tid) {
this->timestamp = timestamp;
« no previous file with comments | « runtime/vm/profiler.h ('k') | runtime/vm/profiler_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698