1 | // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
2 | // for details. All rights reserved. Use of this source code is governed by a |
3 | // BSD-style license that can be found in the LICENSE file. |
4 | |
5 | #include "vm/metrics.h" |
6 | |
7 | #include "vm/isolate.h" |
8 | #include "vm/json_stream.h" |
9 | #include "vm/log.h" |
10 | #include "vm/native_entry.h" |
11 | #include "vm/object.h" |
12 | #include "vm/runtime_entry.h" |
13 | |
14 | namespace dart { |
15 | |
16 | DEFINE_FLAG(bool, |
17 | print_metrics, |
18 | false, |
19 | "Print metrics when isolates (and the VM) are shutdown." ); |
20 | |
21 | Metric* Metric::vm_list_head_ = NULL; |
22 | |
23 | Metric::Metric() : unit_(kCounter), value_(0) {} |
24 | Metric::~Metric() {} |
25 | |
26 | void Metric::InitInstance(IsolateGroup* isolate_group, |
27 | const char* name, |
28 | const char* description, |
29 | Unit unit) { |
30 | // Only called once. |
31 | ASSERT(name != NULL); |
32 | isolate_group_ = isolate_group; |
33 | name_ = name; |
34 | description_ = description; |
35 | unit_ = unit; |
36 | } |
37 | |
38 | #if !defined(PRODUCT) |
39 | void Metric::InitInstance(Isolate* isolate, |
40 | const char* name, |
41 | const char* description, |
42 | Unit unit) { |
43 | // Only called once. |
44 | ASSERT(name != NULL); |
45 | isolate_ = isolate; |
46 | name_ = name; |
47 | description_ = description; |
48 | unit_ = unit; |
49 | } |
50 | |
51 | void Metric::InitInstance(const char* name, |
52 | const char* description, |
53 | Unit unit) { |
54 | // Only called once. |
55 | ASSERT(name != NULL); |
56 | name_ = name; |
57 | description_ = description; |
58 | unit_ = unit; |
59 | } |
60 | |
61 | static const char* UnitString(intptr_t unit) { |
62 | switch (unit) { |
63 | case Metric::kCounter: |
64 | return "counter" ; |
65 | case Metric::kByte: |
66 | return "byte" ; |
67 | case Metric::kMicrosecond: |
68 | return "us" ; |
69 | default: |
70 | UNREACHABLE(); |
71 | } |
72 | UNREACHABLE(); |
73 | return NULL; |
74 | } |
75 | |
76 | void Metric::PrintJSON(JSONStream* stream) { |
77 | JSONObject obj(stream); |
78 | obj.AddProperty("type" , "Counter" ); |
79 | obj.AddProperty("name" , name_); |
80 | obj.AddProperty("description" , description_); |
81 | obj.AddProperty("unit" , UnitString(unit())); |
82 | if (isolate_ == nullptr && isolate_group_ == nullptr) { |
83 | obj.AddFixedServiceId("vm/metrics/%s" , name_); |
84 | } else { |
85 | obj.AddFixedServiceId("metrics/native/%s" , name_); |
86 | } |
87 | // TODO(johnmccutchan): Overflow? |
88 | double value_as_double = static_cast<double>(Value()); |
89 | obj.AddProperty("value" , value_as_double); |
90 | } |
91 | #endif // !defined(PRODUCT) |
92 | |
93 | char* Metric::ValueToString(int64_t value, Unit unit) { |
94 | Thread* thread = Thread::Current(); |
95 | ASSERT(thread != NULL); |
96 | Zone* zone = thread->zone(); |
97 | ASSERT(zone != NULL); |
98 | switch (unit) { |
99 | case kCounter: |
100 | return zone->PrintToString("%" Pd64 "" , value); |
101 | case kByte: { |
102 | const char* scaled_suffix = "B" ; |
103 | double scaled_value = static_cast<double>(value); |
104 | if (value > GB) { |
105 | scaled_suffix = "GB" ; |
106 | scaled_value /= GB; |
107 | } else if (value > MB) { |
108 | scaled_suffix = "MB" ; |
109 | scaled_value /= MB; |
110 | } else if (value > KB) { |
111 | scaled_suffix = "kB" ; |
112 | scaled_value /= KB; |
113 | } |
114 | return zone->PrintToString("%.3f %s (%" Pd64 " B)" , scaled_value, |
115 | scaled_suffix, value); |
116 | } |
117 | case kMicrosecond: { |
118 | const char* scaled_suffix = "us" ; |
119 | double scaled_value = static_cast<double>(value); |
120 | if (value > kMicrosecondsPerSecond) { |
121 | scaled_suffix = "s" ; |
122 | scaled_value /= kMicrosecondsPerSecond; |
123 | } else if (value > kMicrosecondsPerMillisecond) { |
124 | scaled_suffix = "ms" ; |
125 | scaled_value /= kMicrosecondsPerMillisecond; |
126 | } |
127 | return zone->PrintToString("%.3f %s (%" Pd64 " us)" , scaled_value, |
128 | scaled_suffix, value); |
129 | } |
130 | default: |
131 | UNREACHABLE(); |
132 | return NULL; |
133 | } |
134 | } |
135 | |
136 | char* Metric::ToString() { |
137 | Thread* thread = Thread::Current(); |
138 | ASSERT(thread != NULL); |
139 | Zone* zone = thread->zone(); |
140 | ASSERT(zone != NULL); |
141 | return zone->PrintToString("%s %s" , name(), ValueToString(Value(), unit())); |
142 | } |
143 | |
144 | int64_t MetricHeapOldUsed::Value() const { |
145 | ASSERT(isolate_group() == IsolateGroup::Current()); |
146 | return isolate_group()->heap()->UsedInWords(Heap::kOld) * kWordSize; |
147 | } |
148 | |
149 | int64_t MetricHeapOldCapacity::Value() const { |
150 | ASSERT(isolate_group() == IsolateGroup::Current()); |
151 | return isolate_group()->heap()->CapacityInWords(Heap::kOld) * kWordSize; |
152 | } |
153 | |
154 | int64_t MetricHeapOldExternal::Value() const { |
155 | ASSERT(isolate_group() == IsolateGroup::Current()); |
156 | return isolate_group()->heap()->ExternalInWords(Heap::kOld) * kWordSize; |
157 | } |
158 | |
159 | int64_t MetricHeapNewUsed::Value() const { |
160 | ASSERT(isolate_group() == IsolateGroup::Current()); |
161 | return isolate_group()->heap()->UsedInWords(Heap::kNew) * kWordSize; |
162 | } |
163 | |
164 | int64_t MetricHeapNewCapacity::Value() const { |
165 | ASSERT(isolate_group() == IsolateGroup::Current()); |
166 | return isolate_group()->heap()->CapacityInWords(Heap::kNew) * kWordSize; |
167 | } |
168 | |
169 | int64_t MetricHeapNewExternal::Value() const { |
170 | ASSERT(isolate_group() == IsolateGroup::Current()); |
171 | return isolate_group()->heap()->ExternalInWords(Heap::kNew) * kWordSize; |
172 | } |
173 | |
174 | int64_t MetricHeapUsed::Value() const { |
175 | ASSERT(isolate_group() == IsolateGroup::Current()); |
176 | return isolate_group()->heap()->UsedInWords(Heap::kNew) * kWordSize + |
177 | isolate_group()->heap()->UsedInWords(Heap::kOld) * kWordSize; |
178 | } |
179 | |
180 | #if !defined(PRODUCT) |
181 | int64_t MetricIsolateCount::Value() const { |
182 | return Isolate::IsolateListLength(); |
183 | } |
184 | |
185 | int64_t MetricCurrentRSS::() const { |
186 | return Service::CurrentRSS(); |
187 | } |
188 | |
189 | int64_t MetricPeakRSS::() const { |
190 | return Service::MaxRSS(); |
191 | } |
192 | #endif // !defined(PRODUCT) |
193 | |
194 | #if !defined(PRODUCT) |
195 | |
196 | #define VM_METRIC_VARIABLE(type, variable, name, unit) \ |
197 | type vm_metric_##variable; |
198 | VM_METRIC_LIST(VM_METRIC_VARIABLE); |
199 | #undef VM_METRIC_VARIABLE |
200 | |
201 | void Metric::Init() { |
202 | #define VM_METRIC_INIT(type, variable, name, unit) \ |
203 | vm_metric_##variable.InitInstance(name, NULL, Metric::unit); |
204 | VM_METRIC_LIST(VM_METRIC_INIT); |
205 | #undef VM_METRIC_INIT |
206 | } |
207 | |
208 | void Metric::Cleanup() { |
209 | if (FLAG_print_metrics || FLAG_print_benchmarking_metrics) { |
210 | // Create a zone to allocate temporary strings in. |
211 | StackZone sz(Thread::Current()); |
212 | OS::PrintErr("Printing metrics for VM\n" ); |
213 | |
214 | #define VM_METRIC_INIT(type, variable, name, unit) \ |
215 | OS::PrintErr("%s\n", vm_metric_##variable.ToString()); |
216 | VM_METRIC_LIST(VM_METRIC_INIT); |
217 | #undef VM_METRIC_INIT |
218 | OS::PrintErr("\n" ); |
219 | } |
220 | } |
221 | |
222 | #endif // !defined(PRODUCT) |
223 | |
224 | MaxMetric::MaxMetric() : Metric() { |
225 | set_value(kMinInt64); |
226 | } |
227 | |
228 | void MaxMetric::SetValue(int64_t new_value) { |
229 | if (new_value > value()) { |
230 | set_value(new_value); |
231 | } |
232 | } |
233 | |
234 | MinMetric::MinMetric() : Metric() { |
235 | set_value(kMaxInt64); |
236 | } |
237 | |
238 | void MinMetric::SetValue(int64_t new_value) { |
239 | if (new_value < value()) { |
240 | set_value(new_value); |
241 | } |
242 | } |
243 | |
244 | } // namespace dart |
245 | |