| // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| // 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. |
| |
| #if !defined(PRODUCT) |
| |
| #include "vm/metrics.h" |
| |
| #include "vm/isolate.h" |
| #include "vm/json_stream.h" |
| #include "vm/log.h" |
| #include "vm/native_entry.h" |
| #include "vm/object.h" |
| #include "vm/runtime_entry.h" |
| |
| namespace dart { |
| |
| DEFINE_FLAG(bool, |
| print_metrics, |
| false, |
| "Print metrics when isolates (and the VM) are shutdown."); |
| |
| Metric* Metric::vm_list_head_ = NULL; |
| |
| Metric::Metric() |
| : isolate_(NULL), |
| name_(NULL), |
| description_(NULL), |
| unit_(kCounter), |
| value_(0), |
| next_(NULL) {} |
| |
| void Metric::InitInstance(Isolate* isolate, |
| const char* name, |
| const char* description, |
| Unit unit) { |
| // Only called once. |
| ASSERT(next_ == NULL); |
| ASSERT(name != NULL); |
| isolate_ = isolate; |
| name_ = name; |
| description_ = description; |
| unit_ = unit; |
| RegisterWithIsolate(); |
| } |
| |
| void Metric::InitInstance(const char* name, |
| const char* description, |
| Unit unit) { |
| // Only called once. |
| ASSERT(next_ == NULL); |
| ASSERT(name != NULL); |
| name_ = name; |
| description_ = description; |
| unit_ = unit; |
| RegisterWithVM(); |
| } |
| |
| void Metric::CleanupInstance() { |
| // Only deregister metrics which had been registered. Metrics without a name |
| // are from shallow copy isolates. |
| if (name_ != NULL) { |
| if (isolate_ == NULL) { |
| DeregisterWithVM(); |
| } else { |
| DeregisterWithIsolate(); |
| } |
| } |
| } |
| |
| Metric::~Metric() { |
| CleanupInstance(); |
| } |
| |
| #ifndef PRODUCT |
| static const char* UnitString(intptr_t unit) { |
| switch (unit) { |
| case Metric::kCounter: |
| return "counter"; |
| case Metric::kByte: |
| return "byte"; |
| case Metric::kMicrosecond: |
| return "us"; |
| default: |
| UNREACHABLE(); |
| } |
| UNREACHABLE(); |
| return NULL; |
| } |
| |
| void Metric::PrintJSON(JSONStream* stream) { |
| if (!FLAG_support_service) { |
| return; |
| } |
| JSONObject obj(stream); |
| obj.AddProperty("type", "Counter"); |
| obj.AddProperty("name", name_); |
| obj.AddProperty("description", description_); |
| obj.AddProperty("unit", UnitString(unit())); |
| if (isolate_ == NULL) { |
| obj.AddFixedServiceId("vm/metrics/%s", name_); |
| } else { |
| obj.AddFixedServiceId("metrics/native/%s", name_); |
| } |
| // TODO(johnmccutchan): Overflow? |
| double value_as_double = static_cast<double>(Value()); |
| obj.AddProperty("value", value_as_double); |
| } |
| #endif // !PRODUCT |
| |
| char* Metric::ValueToString(int64_t value, Unit unit) { |
| Thread* thread = Thread::Current(); |
| ASSERT(thread != NULL); |
| Zone* zone = thread->zone(); |
| ASSERT(zone != NULL); |
| switch (unit) { |
| case kCounter: |
| return zone->PrintToString("%" Pd64 "", value); |
| case kByte: { |
| const char* scaled_suffix = "B"; |
| double scaled_value = static_cast<double>(value); |
| if (value > GB) { |
| scaled_suffix = "GB"; |
| scaled_value /= GB; |
| } else if (value > MB) { |
| scaled_suffix = "MB"; |
| scaled_value /= MB; |
| } else if (value > KB) { |
| scaled_suffix = "kB"; |
| scaled_value /= KB; |
| } |
| return zone->PrintToString("%.3f %s (%" Pd64 " B)", scaled_value, |
| scaled_suffix, value); |
| } |
| case kMicrosecond: { |
| const char* scaled_suffix = "us"; |
| double scaled_value = static_cast<double>(value); |
| if (value > kMicrosecondsPerSecond) { |
| scaled_suffix = "s"; |
| scaled_value /= kMicrosecondsPerSecond; |
| } else if (value > kMicrosecondsPerMillisecond) { |
| scaled_suffix = "ms"; |
| scaled_value /= kMicrosecondsPerMillisecond; |
| } |
| return zone->PrintToString("%.3f %s (%" Pd64 " us)", scaled_value, |
| scaled_suffix, value); |
| } |
| default: |
| UNREACHABLE(); |
| return NULL; |
| } |
| } |
| |
| char* Metric::ToString() { |
| Thread* thread = Thread::Current(); |
| ASSERT(thread != NULL); |
| Zone* zone = thread->zone(); |
| ASSERT(zone != NULL); |
| return zone->PrintToString("%s %s", name(), ValueToString(Value(), unit())); |
| } |
| |
| bool Metric::NameExists(Metric* head, const char* name) { |
| ASSERT(name != NULL); |
| while (head != NULL) { |
| const char* metric_name = head->name(); |
| ASSERT(metric_name != NULL); |
| if (strcmp(metric_name, name) == 0) { |
| return true; |
| } |
| head = head->next(); |
| } |
| return false; |
| } |
| |
| void Metric::RegisterWithIsolate() { |
| ASSERT(isolate_ != NULL); |
| ASSERT(next_ == NULL); |
| // No duplicate names allowed. |
| ASSERT(!NameExists(isolate_->metrics_list_head(), name())); |
| Metric* head = isolate_->metrics_list_head(); |
| if (head != NULL) { |
| set_next(head); |
| } |
| isolate_->set_metrics_list_head(this); |
| } |
| |
| void Metric::DeregisterWithIsolate() { |
| Metric* head = isolate_->metrics_list_head(); |
| ASSERT(head != NULL); |
| // Handle head of list case. |
| if (head == this) { |
| isolate_->set_metrics_list_head(next()); |
| set_next(NULL); |
| return; |
| } |
| Metric* previous = NULL; |
| while (true) { |
| previous = head; |
| ASSERT(previous != NULL); |
| head = head->next(); |
| if (head == NULL) { |
| break; |
| } |
| if (head == this) { |
| // Remove this from list. |
| previous->set_next(head->next()); |
| set_next(NULL); |
| return; |
| } |
| ASSERT(head != NULL); |
| } |
| UNREACHABLE(); |
| } |
| |
| void Metric::RegisterWithVM() { |
| ASSERT(isolate_ == NULL); |
| ASSERT(next_ == NULL); |
| // No duplicate names allowed. |
| ASSERT(!NameExists(vm_list_head_, name())); |
| Metric* head = vm_list_head_; |
| if (head != NULL) { |
| set_next(head); |
| } |
| vm_list_head_ = this; |
| } |
| |
| void Metric::DeregisterWithVM() { |
| ASSERT(isolate_ == NULL); |
| Metric* head = vm_list_head_; |
| if (head == NULL) { |
| return; |
| } |
| // Handle head of list case. |
| if (head == this) { |
| vm_list_head_ = next(); |
| set_next(NULL); |
| return; |
| } |
| Metric* previous = NULL; |
| while (true) { |
| previous = head; |
| ASSERT(previous != NULL); |
| head = head->next(); |
| if (head == NULL) { |
| break; |
| } |
| if (head == this) { |
| // Remove this from list. |
| previous->set_next(head->next()); |
| set_next(NULL); |
| return; |
| } |
| ASSERT(head != NULL); |
| } |
| UNREACHABLE(); |
| } |
| |
| int64_t MetricHeapOldUsed::Value() const { |
| ASSERT(isolate() == Isolate::Current()); |
| return isolate()->heap()->UsedInWords(Heap::kOld) * kWordSize; |
| } |
| |
| int64_t MetricHeapOldCapacity::Value() const { |
| ASSERT(isolate() == Isolate::Current()); |
| return isolate()->heap()->CapacityInWords(Heap::kOld) * kWordSize; |
| } |
| |
| int64_t MetricHeapOldExternal::Value() const { |
| ASSERT(isolate() == Isolate::Current()); |
| return isolate()->heap()->ExternalInWords(Heap::kOld) * kWordSize; |
| } |
| |
| int64_t MetricHeapNewUsed::Value() const { |
| ASSERT(isolate() == Isolate::Current()); |
| return isolate()->heap()->UsedInWords(Heap::kNew) * kWordSize; |
| } |
| |
| int64_t MetricHeapNewCapacity::Value() const { |
| ASSERT(isolate() == Isolate::Current()); |
| return isolate()->heap()->CapacityInWords(Heap::kNew) * kWordSize; |
| } |
| |
| int64_t MetricHeapNewExternal::Value() const { |
| ASSERT(isolate() == Isolate::Current()); |
| return isolate()->heap()->ExternalInWords(Heap::kNew) * kWordSize; |
| } |
| |
| int64_t MetricHeapUsed::Value() const { |
| ASSERT(isolate() == Isolate::Current()); |
| return isolate()->heap()->UsedInWords(Heap::kNew) * kWordSize + |
| isolate()->heap()->UsedInWords(Heap::kOld) * kWordSize; |
| } |
| |
| int64_t MetricIsolateCount::Value() const { |
| return Isolate::IsolateListLength(); |
| } |
| |
| int64_t MetricCurrentRSS::Value() const { |
| return Service::CurrentRSS(); |
| } |
| |
| int64_t MetricPeakRSS::Value() const { |
| return Service::MaxRSS(); |
| } |
| |
| void Metric::Init() { |
| #define VM_METRIC_INIT(type, variable, name, unit) \ |
| vm_metric_##variable##_.InitInstance(name, NULL, Metric::unit); |
| VM_METRIC_LIST(VM_METRIC_INIT); |
| #undef VM_METRIC_INIT |
| } |
| |
| void Metric::Cleanup() { |
| if (FLAG_print_metrics || FLAG_print_benchmarking_metrics) { |
| // Create a zone to allocate temporary strings in. |
| StackZone sz(Thread::Current()); |
| OS::PrintErr("Printing metrics for VM\n"); |
| Metric* current = Metric::vm_head(); |
| while (current != NULL) { |
| OS::PrintErr("%s\n", current->ToString()); |
| current = current->next(); |
| } |
| OS::PrintErr("\n"); |
| } |
| #define VM_METRIC_CLEANUP(type, variable, name, unit) \ |
| vm_metric_##variable##_.CleanupInstance(); |
| VM_METRIC_LIST(VM_METRIC_CLEANUP); |
| #undef VM_METRIC_CLEANUP |
| } |
| |
| MaxMetric::MaxMetric() : Metric() { |
| set_value(kMinInt64); |
| } |
| |
| void MaxMetric::SetValue(int64_t new_value) { |
| if (new_value > value()) { |
| set_value(new_value); |
| } |
| } |
| |
| MinMetric::MinMetric() : Metric() { |
| set_value(kMaxInt64); |
| } |
| |
| void MinMetric::SetValue(int64_t new_value) { |
| if (new_value < value()) { |
| set_value(new_value); |
| } |
| } |
| |
| } // namespace dart |
| |
| #endif // !defined(PRODUCT) |