OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 #include <cstdio> | 5 #include <cstdio> |
6 | 6 |
7 #include "platform/utils.h" | 7 #include "platform/utils.h" |
8 | 8 |
| 9 #include "vm/atomic.h" |
9 #include "vm/isolate.h" | 10 #include "vm/isolate.h" |
10 #include "vm/json_stream.h" | 11 #include "vm/json_stream.h" |
11 #include "vm/native_symbol.h" | 12 #include "vm/native_symbol.h" |
12 #include "vm/object.h" | 13 #include "vm/object.h" |
13 #include "vm/os.h" | 14 #include "vm/os.h" |
14 #include "vm/profiler.h" | 15 #include "vm/profiler.h" |
15 #include "vm/signal_handler.h" | 16 #include "vm/signal_handler.h" |
| 17 #include "vm/simulator.h" |
16 | 18 |
17 namespace dart { | 19 namespace dart { |
18 | 20 |
19 // Notes on locking and signal handling: | |
20 // | |
21 // The ProfilerManager has a single monitor (monitor_). This monitor guards | |
22 // access to the schedule list of isolates (isolates_, isolates_size_, etc). | |
23 // | |
24 // Each isolate has a mutex (profiler_data_mutex_) which protects access | |
25 // to the isolate's profiler data. | |
26 // | |
27 // Locks can be taken in this order: | |
28 // 1. ProfilerManager::monitor_ | |
29 // 2. isolate->profiler_data_mutex_ | |
30 // In other words, it is not acceptable to take ProfilerManager::monitor_ | |
31 // after grabbing isolate->profiler_data_mutex_. | |
32 // | |
33 // ProfileManager::monitor_ taking entry points: | |
34 // InitOnce, Shutdown | |
35 // ProfilerManager::monitor_ | |
36 // ScheduleIsolate, DescheduleIsolate. | |
37 // ProfilerManager::monitor_, isolate->profiler_data_mutex_ | |
38 // ThreadMain | |
39 // isolate->profiler_data_mutex_ taking entry points: | |
40 // SetupIsolateForProfiling, FreeIsolateForProfiling. | |
41 // ProfilerManager::monitor_, isolate->profiler_data_mutex_ | |
42 // ScheduleIsolate, DescheduleIsolate. | |
43 // ProfilerManager::monitor_, isolate->profiler_data_mutex_ | |
44 // ProfileSignalAction | |
45 // isolate->profiler_data_mutex_ | |
46 // ProfilerManager::monitor_, isolate->profiler_data_mutex_ | |
47 // | |
48 // Signal handling and locking: | |
49 // On OSes with pthreads (Android, Linux, and Mac) we use signal delivery | |
50 // to interrupt the isolate running thread for sampling. After a thread | |
51 // is sent the SIGPROF, it is removed from the scheduled isolate list. | |
52 // Inside the signal handler, after the sample is taken, the isolate is | |
53 // added to the scheduled isolate list again. The side effect of this is | |
54 // that the signal handler must be able to acquire the isolate profiler data | |
55 // mutex and the profile manager monitor. When an isolate running thread | |
56 // (potential signal target) calls into an entry point which acquires | |
57 // ProfileManager::monitor_ signal delivery must be blocked. An example is | |
58 // ProfileManager::ScheduleIsolate which blocks signal delivery while removing | |
59 // the scheduling the isolate. | |
60 // | |
61 | 21 |
62 // Notes on stack frame walking: | 22 // Notes on stack frame walking: |
63 // | 23 // |
64 // The sampling profiler will collect up to Sample::kNumStackFrames stack frames | 24 // The sampling profiler will collect up to Sample::kNumStackFrames stack frames |
65 // The stack frame walking code uses the frame pointer to traverse the stack. | 25 // The stack frame walking code uses the frame pointer to traverse the stack. |
66 // If the VM is compiled without frame pointers (which is the default on | 26 // If the VM is compiled without frame pointers (which is the default on |
67 // recent GCC versions with optimizing enabled) the stack walking code will | 27 // recent GCC versions with optimizing enabled) the stack walking code may |
68 // fail (sometimes leading to a crash). | 28 // fail (sometimes leading to a crash). |
69 // | 29 // |
70 | 30 |
71 DEFINE_FLAG(bool, profile, false, "Enable Sampling Profiler"); | 31 #if defined(USING_SIMULATOR) || defined(TARGET_OS_WINDOWS) || \ |
| 32 defined(TARGET_OS_MACOS) || defined(TARGET_OS_ANDROID) |
| 33 DEFINE_FLAG(bool, profile, false, "Enable Sampling Profiler"); |
| 34 #else |
| 35 DEFINE_FLAG(bool, profile, true, "Enable Sampling Profiler"); |
| 36 #endif |
72 DEFINE_FLAG(bool, trace_profiled_isolates, false, "Trace profiled isolates."); | 37 DEFINE_FLAG(bool, trace_profiled_isolates, false, "Trace profiled isolates."); |
73 | 38 DEFINE_FLAG(charp, profile_dir, NULL, |
74 bool ProfilerManager::initialized_ = false; | 39 "Enable writing profile data into specified directory."); |
75 bool ProfilerManager::shutdown_ = false; | 40 |
76 bool ProfilerManager::thread_running_ = false; | 41 bool Profiler::initialized_ = false; |
77 Monitor* ProfilerManager::monitor_ = NULL; | 42 Monitor* Profiler::monitor_ = NULL; |
78 Monitor* ProfilerManager::start_stop_monitor_ = NULL; | 43 SampleBuffer* Profiler::sample_buffer_ = NULL; |
79 Isolate** ProfilerManager::isolates_ = NULL; | 44 |
80 intptr_t ProfilerManager::isolates_capacity_ = 0; | 45 void Profiler::InitOnce() { |
81 intptr_t ProfilerManager::isolates_size_ = 0; | 46 if (!FLAG_profile) { |
82 | 47 return; |
83 | 48 } |
84 void ProfilerManager::InitOnce() { | 49 ASSERT(!initialized_); |
85 #if defined(USING_SIMULATOR) | 50 initialized_ = true; |
86 // Force disable of profiling on simulator. | 51 monitor_ = new Monitor(); |
87 FLAG_profile = false; | 52 sample_buffer_ = new SampleBuffer(); |
88 #endif | |
89 #if defined(TARGET_OS_WINDOWS) | |
90 // Force disable of profiling on Windows. | |
91 FLAG_profile = false; | |
92 #endif | |
93 if (!FLAG_profile) { | |
94 return; | |
95 } | |
96 NativeSymbolResolver::InitOnce(); | 53 NativeSymbolResolver::InitOnce(); |
97 ASSERT(!initialized_); | 54 ThreadInterrupter::InitOnce(); |
98 monitor_ = new Monitor(); | 55 } |
99 start_stop_monitor_ = new Monitor(); | 56 |
100 initialized_ = true; | 57 |
101 ResizeIsolates(16); | 58 void Profiler::Shutdown() { |
102 if (FLAG_trace_profiled_isolates) { | 59 if (!FLAG_profile) { |
103 OS::Print("ProfilerManager starting up.\n"); | 60 return; |
104 } | 61 } |
| 62 ASSERT(initialized_); |
| 63 ThreadInterrupter::Shutdown(); |
| 64 NativeSymbolResolver::ShutdownOnce(); |
| 65 } |
| 66 |
| 67 |
| 68 void Profiler::InitProfilingForIsolate(Isolate* isolate, bool shared_buffer) { |
| 69 if (!FLAG_profile) { |
| 70 return; |
| 71 } |
| 72 ASSERT(isolate != NULL); |
| 73 ASSERT(sample_buffer_ != NULL); |
| 74 MonitorLocker ml(monitor_); |
105 { | 75 { |
106 ScopedMonitor startup_lock(start_stop_monitor_); | 76 MutexLocker profiler_data_lock(isolate->profiler_data_mutex()); |
107 Thread::Start(ThreadMain, 0); | 77 SampleBuffer* sample_buffer = sample_buffer_; |
108 while (!thread_running_) { | 78 if (!shared_buffer) { |
109 // Wait until profiler thread has started up. | 79 sample_buffer = new SampleBuffer(); |
110 startup_lock.Wait(); | 80 } |
111 } | 81 IsolateProfilerData* profiler_data = |
112 } | 82 new IsolateProfilerData(sample_buffer, !shared_buffer); |
113 if (FLAG_trace_profiled_isolates) { | 83 ASSERT(profiler_data != NULL); |
114 OS::Print("ProfilerManager running.\n"); | 84 isolate->set_profiler_data(profiler_data); |
115 } | 85 if (FLAG_trace_profiled_isolates) { |
116 } | 86 OS::Print("Profiler Setup %p %s\n", isolate, isolate->name()); |
117 | 87 } |
118 | 88 } |
119 void ProfilerManager::Shutdown() { | 89 } |
120 if (!FLAG_profile) { | 90 |
121 return; | 91 |
122 } | 92 void Profiler::ShutdownProfilingForIsolate(Isolate* isolate) { |
123 ASSERT(initialized_); | 93 ASSERT(isolate != NULL); |
124 if (FLAG_trace_profiled_isolates) { | 94 if (!FLAG_profile) { |
125 OS::Print("ProfilerManager shutting down.\n"); | 95 return; |
126 } | 96 } |
127 intptr_t size_at_shutdown = 0; | 97 // We do not have a current isolate. |
| 98 ASSERT(Isolate::Current() == NULL); |
| 99 MonitorLocker ml(monitor_); |
128 { | 100 { |
129 ScopedSignalBlocker ssb; | 101 MutexLocker profiler_data_lock(isolate->profiler_data_mutex()); |
130 { | |
131 ScopedMonitor lock(monitor_); | |
132 shutdown_ = true; | |
133 size_at_shutdown = isolates_size_; | |
134 isolates_size_ = 0; | |
135 free(isolates_); | |
136 isolates_ = NULL; | |
137 lock.Notify(); | |
138 } | |
139 } | |
140 NativeSymbolResolver::ShutdownOnce(); | |
141 { | |
142 ScopedMonitor shutdown_lock(start_stop_monitor_); | |
143 while (thread_running_) { | |
144 // Wait until profiler thread has exited. | |
145 shutdown_lock.Wait(); | |
146 } | |
147 } | |
148 if (FLAG_trace_profiled_isolates) { | |
149 OS::Print("ProfilerManager shut down (%" Pd ").\n", size_at_shutdown); | |
150 } | |
151 } | |
152 | |
153 | |
154 void ProfilerManager::SetupIsolateForProfiling(Isolate* isolate) { | |
155 if (!FLAG_profile) { | |
156 return; | |
157 } | |
158 ASSERT(isolate != NULL); | |
159 { | |
160 ScopedSignalBlocker ssb; | |
161 { | |
162 ScopedMutex profiler_data_lock(isolate->profiler_data_mutex()); | |
163 SampleBuffer* sample_buffer = new SampleBuffer(); | |
164 ASSERT(sample_buffer != NULL); | |
165 IsolateProfilerData* profiler_data = | |
166 new IsolateProfilerData(isolate, sample_buffer); | |
167 ASSERT(profiler_data != NULL); | |
168 profiler_data->set_sample_interval_micros(1000); | |
169 isolate->set_profiler_data(profiler_data); | |
170 if (FLAG_trace_profiled_isolates) { | |
171 OS::Print("ProfilerManager Setup Isolate %p %s %p\n", | |
172 isolate, | |
173 isolate->name(), | |
174 reinterpret_cast<void*>(Thread::GetCurrentThreadId())); | |
175 } | |
176 } | |
177 } | |
178 } | |
179 | |
180 | |
181 void ProfilerManager::FreeIsolateProfilingData(Isolate* isolate) { | |
182 ScopedMutex profiler_data_lock(isolate->profiler_data_mutex()); | |
183 IsolateProfilerData* profiler_data = isolate->profiler_data(); | |
184 if (profiler_data == NULL) { | |
185 // Already freed. | |
186 return; | |
187 } | |
188 isolate->set_profiler_data(NULL); | |
189 SampleBuffer* sample_buffer = profiler_data->sample_buffer(); | |
190 ASSERT(sample_buffer != NULL); | |
191 profiler_data->set_sample_buffer(NULL); | |
192 delete sample_buffer; | |
193 delete profiler_data; | |
194 if (FLAG_trace_profiled_isolates) { | |
195 OS::Print("ProfilerManager Shutdown Isolate %p %s %p\n", | |
196 isolate, | |
197 isolate->name(), | |
198 reinterpret_cast<void*>(Thread::GetCurrentThreadId())); | |
199 } | |
200 } | |
201 | |
202 | |
203 void ProfilerManager::ShutdownIsolateForProfiling(Isolate* isolate) { | |
204 ASSERT(isolate != NULL); | |
205 if (!FLAG_profile) { | |
206 return; | |
207 } | |
208 { | |
209 ScopedSignalBlocker ssb; | |
210 FreeIsolateProfilingData(isolate); | |
211 } | |
212 } | |
213 | |
214 | |
215 void ProfilerManager::ScheduleIsolateHelper(Isolate* isolate) { | |
216 ScopedMonitor lock(monitor_); | |
217 { | |
218 if (shutdown_) { | |
219 // Shutdown. | |
220 return; | |
221 } | |
222 ScopedMutex profiler_data_lock(isolate->profiler_data_mutex()); | |
223 IsolateProfilerData* profiler_data = isolate->profiler_data(); | 102 IsolateProfilerData* profiler_data = isolate->profiler_data(); |
224 if (profiler_data == NULL) { | 103 if (profiler_data == NULL) { |
| 104 // Already freed. |
225 return; | 105 return; |
226 } | 106 } |
227 profiler_data->Scheduled(OS::GetCurrentTimeMicros(), | 107 isolate->set_profiler_data(NULL); |
228 Thread::GetCurrentThreadId()); | 108 profiler_data->set_sample_buffer(NULL); |
229 } | 109 delete profiler_data; |
230 intptr_t i = FindIsolate(isolate); | 110 if (FLAG_trace_profiled_isolates) { |
231 if (i >= 0) { | 111 OS::Print("Profiler Shutdown %p %s\n", isolate, isolate->name()); |
232 // Already scheduled. | 112 } |
233 return; | 113 } |
234 } | 114 } |
235 AddIsolate(isolate); | 115 |
236 lock.Notify(); | 116 |
237 } | 117 void Profiler::BeginExecution(Isolate* isolate) { |
238 | 118 if (isolate == NULL) { |
239 | 119 return; |
240 void ProfilerManager::ScheduleIsolate(Isolate* isolate, bool inside_signal) { | 120 } |
241 if (!FLAG_profile) { | 121 if (!FLAG_profile) { |
242 return; | 122 return; |
243 } | 123 } |
244 ASSERT(initialized_); | 124 ASSERT(initialized_); |
245 ASSERT(isolate != NULL); | 125 IsolateProfilerData* profiler_data = isolate->profiler_data(); |
246 if (!inside_signal) { | 126 if (profiler_data == NULL) { |
247 ScopedSignalBlocker ssb; | 127 return; |
248 { | 128 } |
249 ScheduleIsolateHelper(isolate); | 129 SampleBuffer* sample_buffer = profiler_data->sample_buffer(); |
250 } | 130 if (sample_buffer == NULL) { |
251 } else { | 131 return; |
252 // Do not need a signal blocker inside a signal handler. | 132 } |
253 { | 133 Sample* sample = sample_buffer->ReserveSample(); |
254 ScheduleIsolateHelper(isolate); | 134 sample->Init(Sample::kIsolateStart, isolate, OS::GetCurrentTimeMicros(), |
255 } | 135 Thread::GetCurrentThreadId()); |
256 } | 136 ThreadInterrupter::Register(RecordSampleInterruptCallback, isolate); |
257 } | 137 } |
258 | 138 |
259 | 139 |
260 void ProfilerManager::DescheduleIsolate(Isolate* isolate) { | 140 void Profiler::EndExecution(Isolate* isolate) { |
| 141 if (isolate == NULL) { |
| 142 return; |
| 143 } |
261 if (!FLAG_profile) { | 144 if (!FLAG_profile) { |
262 return; | 145 return; |
263 } | 146 } |
264 ASSERT(initialized_); | 147 ASSERT(initialized_); |
265 ASSERT(isolate != NULL); | 148 ThreadInterrupter::Unregister(); |
266 { | 149 IsolateProfilerData* profiler_data = isolate->profiler_data(); |
267 ScopedSignalBlocker ssb; | 150 if (profiler_data == NULL) { |
268 { | 151 return; |
269 ScopedMonitor lock(monitor_); | 152 } |
270 if (shutdown_) { | 153 SampleBuffer* sample_buffer = profiler_data->sample_buffer(); |
271 // Shutdown. | 154 if (sample_buffer == NULL) { |
272 return; | 155 return; |
273 } | 156 } |
274 intptr_t i = FindIsolate(isolate); | 157 Sample* sample = sample_buffer->ReserveSample(); |
275 if (i < 0) { | 158 sample->Init(Sample::kIsolateStop, isolate, OS::GetCurrentTimeMicros(), |
276 // Not scheduled. | 159 Thread::GetCurrentThreadId()); |
277 return; | 160 } |
278 } | 161 |
279 { | 162 |
280 ScopedMutex profiler_data_lock(isolate->profiler_data_mutex()); | 163 void Profiler::RecordTickInterruptCallback(const InterruptedThreadState& state, |
281 IsolateProfilerData* profiler_data = isolate->profiler_data(); | 164 void* data) { |
282 if (profiler_data != NULL) { | 165 Isolate* isolate = reinterpret_cast<Isolate*>(data); |
283 profiler_data->Descheduled(); | 166 if (isolate == NULL) { |
284 } | 167 return; |
285 } | 168 } |
286 RemoveIsolate(i); | 169 IsolateProfilerData* profiler_data = isolate->profiler_data(); |
287 lock.Notify(); | 170 if (profiler_data == NULL) { |
288 } | 171 return; |
289 } | 172 } |
290 } | 173 SampleBuffer* sample_buffer = profiler_data->sample_buffer(); |
291 | 174 if (sample_buffer == NULL) { |
292 | 175 return; |
293 void PrintToJSONStream(Isolate* isolate, JSONStream* stream) { | 176 } |
| 177 Sample* sample = sample_buffer->ReserveSample(); |
| 178 sample->Init(Sample::kIsolateSample, isolate, OS::GetCurrentTimeMicros(), |
| 179 state.tid); |
| 180 } |
| 181 |
| 182 |
| 183 void Profiler::RecordSampleInterruptCallback( |
| 184 const InterruptedThreadState& state, |
| 185 void* data) { |
| 186 Isolate* isolate = reinterpret_cast<Isolate*>(data); |
| 187 if (isolate == NULL) { |
| 188 return; |
| 189 } |
| 190 IsolateProfilerData* profiler_data = isolate->profiler_data(); |
| 191 if (profiler_data == NULL) { |
| 192 return; |
| 193 } |
| 194 SampleBuffer* sample_buffer = profiler_data->sample_buffer(); |
| 195 if (sample_buffer == NULL) { |
| 196 return; |
| 197 } |
| 198 Sample* sample = sample_buffer->ReserveSample(); |
| 199 sample->Init(Sample::kIsolateSample, isolate, OS::GetCurrentTimeMicros(), |
| 200 state.tid); |
| 201 uintptr_t stack_lower = 0; |
| 202 uintptr_t stack_upper = 0; |
| 203 isolate->GetStackBounds(&stack_lower, &stack_upper); |
| 204 if ((stack_lower == 0) || (stack_upper == 0)) { |
| 205 stack_lower = 0; |
| 206 stack_upper = 0; |
| 207 } |
| 208 ProfilerSampleStackWalker stackWalker(sample, stack_lower, stack_upper, |
| 209 state.pc, state.fp, state.sp); |
| 210 stackWalker.walk(); |
| 211 } |
| 212 |
| 213 |
| 214 void Profiler::PrintToJSONStream(Isolate* isolate, JSONStream* stream) { |
294 ASSERT(isolate == Isolate::Current()); | 215 ASSERT(isolate == Isolate::Current()); |
295 { | |
296 // We can't get signals here. | |
297 } | |
298 UNIMPLEMENTED(); | 216 UNIMPLEMENTED(); |
299 } | 217 } |
300 | 218 |
301 | 219 |
302 void ProfilerManager::ResizeIsolates(intptr_t new_capacity) { | |
303 ASSERT(new_capacity < kMaxProfiledIsolates); | |
304 ASSERT(new_capacity > isolates_capacity_); | |
305 Isolate* isolate = NULL; | |
306 isolates_ = reinterpret_cast<Isolate**>( | |
307 realloc(isolates_, sizeof(isolate) * new_capacity)); | |
308 isolates_capacity_ = new_capacity; | |
309 } | |
310 | |
311 | |
312 void ProfilerManager::AddIsolate(Isolate* isolate) { | |
313 // Must be called with monitor_ locked. | |
314 if (isolates_ == NULL) { | |
315 // We are shutting down. | |
316 return; | |
317 } | |
318 if (isolates_size_ == isolates_capacity_) { | |
319 ResizeIsolates(isolates_capacity_ == 0 ? 16 : isolates_capacity_ * 2); | |
320 } | |
321 isolates_[isolates_size_] = isolate; | |
322 isolates_size_++; | |
323 } | |
324 | |
325 | |
326 intptr_t ProfilerManager::FindIsolate(Isolate* isolate) { | |
327 // Must be called with monitor_ locked. | |
328 if (isolates_ == NULL) { | |
329 // We are shutting down. | |
330 return -1; | |
331 } | |
332 for (intptr_t i = 0; i < isolates_size_; i++) { | |
333 if (isolates_[i] == isolate) { | |
334 return i; | |
335 } | |
336 } | |
337 return -1; | |
338 } | |
339 | |
340 | |
341 void ProfilerManager::RemoveIsolate(intptr_t i) { | |
342 // Must be called with monitor_ locked. | |
343 if (isolates_ == NULL) { | |
344 // We are shutting down. | |
345 return; | |
346 } | |
347 ASSERT(i < isolates_size_); | |
348 intptr_t last = isolates_size_ - 1; | |
349 if (i != last) { | |
350 isolates_[i] = isolates_[last]; | |
351 } | |
352 // Mark last as NULL. | |
353 isolates_[last] = NULL; | |
354 // Pop. | |
355 isolates_size_--; | |
356 } | |
357 | |
358 | |
359 static char* FindSymbolName(uintptr_t pc, bool* native_symbol) { | 220 static char* FindSymbolName(uintptr_t pc, bool* native_symbol) { |
360 // TODO(johnmccutchan): Differentiate between symbols which can't be found | 221 // TODO(johnmccutchan): Differentiate between symbols which can't be found |
361 // and symbols which were GCed. (Heap::CodeContains). | 222 // and symbols which were GCed. (Heap::CodeContains). |
362 ASSERT(native_symbol != NULL); | 223 ASSERT(native_symbol != NULL); |
363 const char* symbol_name = "Unknown"; | 224 const char* symbol_name = "Unknown"; |
364 *native_symbol = false; | 225 *native_symbol = false; |
| 226 if (pc == 0) { |
| 227 return const_cast<char*>(Sample::kNoFrame); |
| 228 } |
365 const Code& code = Code::Handle(Code::LookupCode(pc)); | 229 const Code& code = Code::Handle(Code::LookupCode(pc)); |
366 if (code.IsNull()) { | 230 if (code.IsNull()) { |
367 // Possibly a native symbol. | 231 // Possibly a native symbol. |
368 char* native_name = NativeSymbolResolver::LookupSymbolName(pc); | 232 char* native_name = NativeSymbolResolver::LookupSymbolName(pc); |
369 if (native_name != NULL) { | 233 if (native_name != NULL) { |
370 symbol_name = native_name; | 234 symbol_name = native_name; |
371 *native_symbol = true; | 235 *native_symbol = true; |
372 } | 236 } |
373 } else { | 237 } else { |
374 const Function& function = Function::Handle(code.function()); | 238 const Function& function = Function::Handle(code.function()); |
375 if (!function.IsNull()) { | 239 if (!function.IsNull()) { |
376 const String& name = String::Handle(function.QualifiedUserVisibleName()); | 240 const String& name = String::Handle(function.QualifiedUserVisibleName()); |
377 if (!name.IsNull()) { | 241 if (!name.IsNull()) { |
378 symbol_name = name.ToCString(); | 242 symbol_name = name.ToCString(); |
379 } | 243 } |
380 } | 244 } |
381 } | 245 } |
382 return const_cast<char*>(symbol_name); | 246 return const_cast<char*>(symbol_name); |
383 } | 247 } |
384 | 248 |
385 | 249 |
386 void ProfilerManager::WriteTracing(Isolate* isolate, const char* name, | 250 void Profiler::WriteTracingSample(Isolate* isolate, intptr_t pid, |
387 Dart_Port port) { | 251 Sample* sample, JSONArray& events) { |
388 ASSERT(isolate == Isolate::Current()); | 252 Sample::SampleType type = sample->type; |
389 { | 253 intptr_t tid = Thread::ThreadIdToIntPtr(sample->tid); |
390 ScopedSignalBlocker ssb; | 254 double timestamp = static_cast<double>(sample->timestamp); |
391 { | 255 const char* isolate_name = isolate->name(); |
392 ScopedMutex profiler_data_lock(isolate->profiler_data_mutex()); | 256 switch (type) { |
393 IsolateProfilerData* profiler_data = isolate->profiler_data(); | 257 case Sample::kIsolateStart: { |
394 if (profiler_data == NULL) { | 258 JSONObject begin(&events); |
395 return; | 259 begin.AddProperty("ph", "B"); |
396 } | 260 begin.AddProperty("tid", tid); |
397 SampleBuffer* sample_buffer = profiler_data->sample_buffer(); | 261 begin.AddProperty("pid", pid); |
398 ASSERT(sample_buffer != NULL); | 262 begin.AddProperty("name", isolate_name); |
399 JSONStream stream(10 * MB); | 263 begin.AddProperty("ts", timestamp); |
400 intptr_t tid = reinterpret_cast<intptr_t>(sample_buffer); | 264 } |
401 intptr_t pid = 1; | 265 break; |
402 { | 266 case Sample::kIsolateStop: { |
403 JSONArray events(&stream); | 267 JSONObject begin(&events); |
| 268 begin.AddProperty("ph", "E"); |
| 269 begin.AddProperty("tid", tid); |
| 270 begin.AddProperty("pid", pid); |
| 271 begin.AddProperty("name", isolate_name); |
| 272 begin.AddProperty("ts", timestamp); |
| 273 } |
| 274 break; |
| 275 case Sample::kIsolateSample: |
| 276 // Write "B" events. |
| 277 for (int i = Sample::kNumStackFrames - 1; i >= 0; i--) { |
| 278 bool native_symbol = false; |
| 279 char* symbol_name = FindSymbolName(sample->pcs[i], &native_symbol); |
404 { | 280 { |
405 JSONObject thread_name(&events); | 281 JSONObject begin(&events); |
406 thread_name.AddProperty("name", "thread_name"); | 282 begin.AddProperty("ph", "B"); |
407 thread_name.AddProperty("ph", "M"); | 283 begin.AddProperty("tid", tid); |
408 thread_name.AddProperty("tid", tid); | 284 begin.AddProperty("pid", pid); |
409 thread_name.AddProperty("pid", pid); | 285 begin.AddProperty("name", symbol_name); |
410 { | 286 begin.AddProperty("ts", timestamp); |
411 JSONObject args(&thread_name, "args"); | |
412 args.AddProperty("name", name); | |
413 } | |
414 } | 287 } |
415 { | 288 if (native_symbol) { |
416 JSONObject process_name(&events); | 289 NativeSymbolResolver::FreeSymbolName(symbol_name); |
417 process_name.AddProperty("name", "process_name"); | |
418 process_name.AddProperty("ph", "M"); | |
419 process_name.AddProperty("tid", tid); | |
420 process_name.AddProperty("pid", pid); | |
421 { | |
422 JSONObject args(&process_name, "args"); | |
423 args.AddProperty("name", "Dart VM"); | |
424 } | |
425 } | |
426 uint64_t last_time = 0; | |
427 for (Sample* i = sample_buffer->FirstSample(); | |
428 i != sample_buffer->LastSample(); | |
429 i = sample_buffer->NextSample(i)) { | |
430 if (last_time == 0) { | |
431 last_time = i->timestamp; | |
432 } | |
433 intptr_t delta = i->timestamp - last_time; | |
434 { | |
435 double percentage = static_cast<double>(i->cpu_usage) / | |
436 static_cast<double>(delta) * 100.0; | |
437 if (percentage != percentage) { | |
438 percentage = 0.0; | |
439 } | |
440 percentage = percentage < 0.0 ? 0.0 : percentage; | |
441 percentage = percentage > 100.0 ? 100.0 : percentage; | |
442 { | |
443 JSONObject cpu_usage(&events); | |
444 cpu_usage.AddProperty("name", "CPU Usage"); | |
445 cpu_usage.AddProperty("ph", "C"); | |
446 cpu_usage.AddProperty("tid", tid); | |
447 cpu_usage.AddProperty("pid", pid); | |
448 cpu_usage.AddProperty("ts", static_cast<double>(last_time)); | |
449 { | |
450 JSONObject args(&cpu_usage, "args"); | |
451 args.AddProperty("CPU", percentage); | |
452 } | |
453 } | |
454 { | |
455 JSONObject cpu_usage(&events); | |
456 cpu_usage.AddProperty("name", "CPU Usage"); | |
457 cpu_usage.AddProperty("ph", "C"); | |
458 cpu_usage.AddProperty("tid", tid); | |
459 cpu_usage.AddProperty("pid", pid); | |
460 cpu_usage.AddProperty("ts", static_cast<double>(i->timestamp)); | |
461 { | |
462 JSONObject args(&cpu_usage, "args"); | |
463 args.AddProperty("CPU", percentage); | |
464 } | |
465 } | |
466 } | |
467 for (int j = 0; j < Sample::kNumStackFrames; j++) { | |
468 if (i->pcs[j] == 0) { | |
469 continue; | |
470 } | |
471 bool native_symbol = false; | |
472 char* symbol_name = FindSymbolName(i->pcs[j], &native_symbol); | |
473 { | |
474 JSONObject begin(&events); | |
475 begin.AddProperty("ph", "B"); | |
476 begin.AddProperty("tid", tid); | |
477 begin.AddProperty("pid", pid); | |
478 begin.AddProperty("name", symbol_name); | |
479 begin.AddProperty("ts", static_cast<double>(last_time)); | |
480 } | |
481 if (native_symbol) { | |
482 NativeSymbolResolver::FreeSymbolName(symbol_name); | |
483 } | |
484 } | |
485 for (int j = Sample::kNumStackFrames-1; j >= 0; j--) { | |
486 if (i->pcs[j] == 0) { | |
487 continue; | |
488 } | |
489 bool native_symbol = false; | |
490 char* symbol_name = FindSymbolName(i->pcs[j], &native_symbol); | |
491 { | |
492 JSONObject end(&events); | |
493 end.AddProperty("ph", "E"); | |
494 end.AddProperty("tid", tid); | |
495 end.AddProperty("pid", pid); | |
496 end.AddProperty("name", symbol_name); | |
497 end.AddProperty("ts", static_cast<double>(i->timestamp)); | |
498 } | |
499 if (native_symbol) { | |
500 NativeSymbolResolver::FreeSymbolName(symbol_name); | |
501 } | |
502 } | |
503 last_time = i->timestamp; | |
504 } | 290 } |
505 } | 291 } |
506 char fname[1024]; | 292 // Write "E" events. |
507 #if defined(TARGET_OS_WINDOWS) | 293 for (int i = 0; i < Sample::kNumStackFrames; i++) { |
508 snprintf(fname, sizeof(fname)-1, "c:\\tmp\\isolate-%d.prof", | 294 bool native_symbol = false; |
509 static_cast<int>(port)); | 295 char* symbol_name = FindSymbolName(sample->pcs[i], &native_symbol); |
510 #else | 296 { |
511 snprintf(fname, sizeof(fname)-1, "/tmp/isolate-%d.prof", | 297 JSONObject begin(&events); |
512 static_cast<int>(port)); | 298 begin.AddProperty("ph", "E"); |
513 #endif | 299 begin.AddProperty("tid", tid); |
514 printf("%s\n", fname); | 300 begin.AddProperty("pid", pid); |
515 FILE* f = fopen(fname, "wb"); | 301 begin.AddProperty("name", symbol_name); |
516 ASSERT(f != NULL); | 302 begin.AddProperty("ts", timestamp); |
517 fputs(stream.ToCString(), f); | 303 } |
518 fclose(f); | 304 if (native_symbol) { |
519 } | 305 NativeSymbolResolver::FreeSymbolName(symbol_name); |
| 306 } |
| 307 } |
| 308 break; |
| 309 default: |
| 310 UNIMPLEMENTED(); |
520 } | 311 } |
521 } | 312 } |
522 | 313 |
523 | 314 |
524 IsolateProfilerData::IsolateProfilerData(Isolate* isolate, | 315 void Profiler::WriteTracing(Isolate* isolate) { |
525 SampleBuffer* sample_buffer) { | 316 if (isolate == NULL) { |
526 isolate_ = isolate; | 317 return; |
| 318 } |
| 319 if (!FLAG_profile) { |
| 320 return; |
| 321 } |
| 322 ASSERT(initialized_); |
| 323 if (FLAG_profile_dir == NULL) { |
| 324 return; |
| 325 } |
| 326 Dart_FileOpenCallback file_open = Isolate::file_open_callback(); |
| 327 Dart_FileCloseCallback file_close = Isolate::file_close_callback(); |
| 328 Dart_FileWriteCallback file_write = Isolate::file_write_callback(); |
| 329 if ((file_open == NULL) || (file_close == NULL) || (file_write == NULL)) { |
| 330 // Embedder has not provided necessary callbacks. |
| 331 return; |
| 332 } |
| 333 // We will be looking up code objects within the isolate. |
| 334 ASSERT(Isolate::Current() != NULL); |
| 335 // We do not want to be interrupted while processing the buffer. |
| 336 EndExecution(isolate); |
| 337 MutexLocker profiler_data_lock(isolate->profiler_data_mutex()); |
| 338 IsolateProfilerData* profiler_data = isolate->profiler_data(); |
| 339 if (profiler_data == NULL) { |
| 340 return; |
| 341 } |
| 342 SampleBuffer* sample_buffer = profiler_data->sample_buffer(); |
| 343 ASSERT(sample_buffer != NULL); |
| 344 JSONStream stream(10 * MB); |
| 345 intptr_t pid = OS::ProcessId(); |
| 346 { |
| 347 JSONArray events(&stream); |
| 348 { |
| 349 JSONObject process_name(&events); |
| 350 process_name.AddProperty("name", "process_name"); |
| 351 process_name.AddProperty("ph", "M"); |
| 352 process_name.AddProperty("pid", pid); |
| 353 { |
| 354 JSONObject args(&process_name, "args"); |
| 355 args.AddProperty("name", "Dart VM"); |
| 356 } |
| 357 } |
| 358 for (intptr_t i = 0; i < sample_buffer->capacity(); i++) { |
| 359 Sample* sample = sample_buffer->GetSample(i); |
| 360 if (sample->isolate != isolate) { |
| 361 continue; |
| 362 } |
| 363 if (sample->timestamp == 0) { |
| 364 continue; |
| 365 } |
| 366 WriteTracingSample(isolate, pid, sample, events); |
| 367 } |
| 368 } |
| 369 const char* format = "%s/dart-profile-%" Pd "-%" Pd ".json"; |
| 370 intptr_t len = OS::SNPrint(NULL, 0, format, |
| 371 FLAG_profile_dir, pid, isolate->main_port()); |
| 372 char* filename = Isolate::Current()->current_zone()->Alloc<char>(len + 1); |
| 373 OS::SNPrint(filename, len + 1, format, |
| 374 FLAG_profile_dir, pid, isolate->main_port()); |
| 375 void* f = file_open(filename, true); |
| 376 if (f == NULL) { |
| 377 // Cannot write. |
| 378 return; |
| 379 } |
| 380 TextBuffer* buffer = stream.buffer(); |
| 381 ASSERT(buffer != NULL); |
| 382 file_write(buffer->buf(), buffer->length(), f); |
| 383 file_close(f); |
| 384 BeginExecution(isolate); |
| 385 } |
| 386 |
| 387 |
| 388 IsolateProfilerData::IsolateProfilerData(SampleBuffer* sample_buffer, |
| 389 bool own_sample_buffer) { |
527 sample_buffer_ = sample_buffer; | 390 sample_buffer_ = sample_buffer; |
528 timer_expiration_micros_ = kNoExpirationTime; | 391 own_sample_buffer_ = own_sample_buffer; |
529 last_sampled_micros_ = 0; | |
530 thread_id_ = 0; | |
531 } | 392 } |
532 | 393 |
533 | 394 |
534 IsolateProfilerData::~IsolateProfilerData() { | 395 IsolateProfilerData::~IsolateProfilerData() { |
535 } | 396 if (own_sample_buffer_) { |
536 | 397 delete sample_buffer_; |
537 | 398 sample_buffer_ = NULL; |
538 void IsolateProfilerData::SampledAt(int64_t current_time) { | 399 own_sample_buffer_ = false; |
539 last_sampled_micros_ = current_time; | 400 } |
540 } | |
541 | |
542 | |
543 void IsolateProfilerData::Scheduled(int64_t current_time, ThreadId thread_id) { | |
544 timer_expiration_micros_ = current_time + sample_interval_micros_; | |
545 thread_id_ = thread_id; | |
546 Thread::GetThreadCpuUsage(thread_id_, &cpu_usage_); | |
547 } | |
548 | |
549 | |
550 void IsolateProfilerData::Descheduled() { | |
551 // TODO(johnmccutchan): Track when we ran for a fraction of our sample | |
552 // interval and incorporate the time difference when scheduling the | |
553 // isolate again. | |
554 cpu_usage_ = kDescheduledCpuUsage; | |
555 timer_expiration_micros_ = kNoExpirationTime; | |
556 Sample* sample = sample_buffer_->ReserveSample(); | |
557 ASSERT(sample != NULL); | |
558 sample->timestamp = OS::GetCurrentTimeMicros(); | |
559 sample->cpu_usage = 0; | |
560 sample->vm_tags = Sample::kIdle; | |
561 } | 401 } |
562 | 402 |
563 | 403 |
564 const char* Sample::kLookupSymbol = "Symbol Not Looked Up"; | 404 const char* Sample::kLookupSymbol = "Symbol Not Looked Up"; |
565 const char* Sample::kNoSymbol = "No Symbol Found"; | 405 const char* Sample::kNoSymbol = "No Symbol Found"; |
| 406 const char* Sample::kNoFrame = "<no frame>"; |
566 | 407 |
567 Sample::Sample() { | 408 void Sample::Init(SampleType type, Isolate* isolate, int64_t timestamp, |
568 timestamp = 0; | 409 ThreadId tid) { |
569 cpu_usage = 0; | 410 this->timestamp = timestamp; |
570 for (int i = 0; i < kNumStackFrames; i++) { | 411 this->tid = tid; |
| 412 this->isolate = isolate; |
| 413 for (intptr_t i = 0; i < kNumStackFrames; i++) { |
571 pcs[i] = 0; | 414 pcs[i] = 0; |
572 } | 415 } |
573 vm_tags = kIdle; | 416 this->type = type; |
| 417 vm_tags = 0; |
574 runtime_tags = 0; | 418 runtime_tags = 0; |
575 } | 419 } |
576 | 420 |
577 | |
578 SampleBuffer::SampleBuffer(intptr_t capacity) { | 421 SampleBuffer::SampleBuffer(intptr_t capacity) { |
579 start_ = 0; | |
580 end_ = 0; | |
581 capacity_ = capacity; | 422 capacity_ = capacity; |
582 samples_ = reinterpret_cast<Sample*>(calloc(capacity, sizeof(Sample))); | 423 samples_ = reinterpret_cast<Sample*>(calloc(capacity, sizeof(Sample))); |
| 424 cursor_ = 0; |
583 } | 425 } |
584 | 426 |
585 | 427 |
586 SampleBuffer::~SampleBuffer() { | 428 SampleBuffer::~SampleBuffer() { |
587 if (samples_ != NULL) { | 429 if (samples_ != NULL) { |
588 free(samples_); | 430 free(samples_); |
589 samples_ = NULL; | 431 samples_ = NULL; |
590 start_ = 0; | 432 cursor_ = 0; |
591 end_ = 0; | |
592 capacity_ = 0; | 433 capacity_ = 0; |
593 } | 434 } |
594 } | 435 } |
595 | 436 |
596 | 437 |
597 Sample* SampleBuffer::ReserveSample() { | 438 Sample* SampleBuffer::ReserveSample() { |
598 ASSERT(samples_ != NULL); | 439 ASSERT(samples_ != NULL); |
599 intptr_t index = end_; | 440 uintptr_t cursor = AtomicOperations::FetchAndIncrement(&cursor_); |
600 end_ = WrapIncrement(end_); | 441 // Map back into sample buffer range. |
601 if (end_ == start_) { | 442 cursor = cursor % capacity_; |
602 start_ = WrapIncrement(start_); | 443 return &samples_[cursor]; |
603 } | |
604 ASSERT(index >= 0); | |
605 ASSERT(index < capacity_); | |
606 // Reset. | |
607 samples_[index] = Sample(); | |
608 return &samples_[index]; | |
609 } | 444 } |
610 | 445 |
611 | 446 |
612 Sample* SampleBuffer::FirstSample() const { | |
613 return &samples_[start_]; | |
614 } | |
615 | |
616 | |
617 Sample* SampleBuffer::NextSample(Sample* sample) const { | |
618 ASSERT(sample >= &samples_[0]); | |
619 ASSERT(sample < &samples_[capacity_]); | |
620 intptr_t index = sample - samples_; | |
621 index = WrapIncrement(index); | |
622 return &samples_[index]; | |
623 } | |
624 | |
625 | |
626 Sample* SampleBuffer::LastSample() const { | |
627 return &samples_[end_]; | |
628 } | |
629 | |
630 | |
631 intptr_t SampleBuffer::WrapIncrement(intptr_t i) const { | |
632 return (i + 1) % capacity_; | |
633 } | |
634 | |
635 | |
636 ProfilerSampleStackWalker::ProfilerSampleStackWalker(Sample* sample, | 447 ProfilerSampleStackWalker::ProfilerSampleStackWalker(Sample* sample, |
637 uintptr_t stack_lower, | 448 uintptr_t stack_lower, |
638 uintptr_t stack_upper, | 449 uintptr_t stack_upper, |
639 uintptr_t pc, | 450 uintptr_t pc, |
640 uintptr_t fp, | 451 uintptr_t fp, |
641 uintptr_t sp) : | 452 uintptr_t sp) : |
642 sample_(sample), | 453 sample_(sample), |
643 stack_lower_(stack_lower), | 454 stack_lower_(stack_lower), |
644 stack_upper_(stack_upper), | 455 stack_upper_(stack_upper), |
645 original_pc_(pc), | 456 original_pc_(pc), |
646 original_fp_(fp), | 457 original_fp_(fp), |
647 original_sp_(sp), | 458 original_sp_(sp), |
648 lower_bound_(stack_lower) { | 459 lower_bound_(stack_lower) { |
649 ASSERT(sample_ != NULL); | 460 ASSERT(sample_ != NULL); |
650 } | 461 } |
651 | 462 |
652 | 463 |
653 int ProfilerSampleStackWalker::walk() { | 464 int ProfilerSampleStackWalker::walk() { |
654 uword* pc = reinterpret_cast<uword*>(original_pc_); | 465 uword* pc = reinterpret_cast<uword*>(original_pc_); |
| 466 #define WALK_STACK |
655 #if defined(WALK_STACK) | 467 #if defined(WALK_STACK) |
656 uword* fp = reinterpret_cast<uword*>(original_fp_); | 468 uword* fp = reinterpret_cast<uword*>(original_fp_); |
657 uword* previous_fp = fp; | 469 uword* previous_fp = fp; |
658 if (original_sp_ < lower_bound_) { | 470 if (original_sp_ < lower_bound_) { |
659 // The stack pointer gives us a better lower bound than | 471 // The stack pointer gives us a better lower bound than |
660 // the isolates stack limit. | 472 // the isolates stack limit. |
661 lower_bound_ = original_sp_; | 473 lower_bound_ = original_sp_; |
662 } | 474 } |
663 int i = 0; | 475 int i = 0; |
664 for (; i < Sample::kNumStackFrames; i++) { | 476 for (; i < Sample::kNumStackFrames; i++) { |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
701 return false; | 513 return false; |
702 } | 514 } |
703 uintptr_t cursor = reinterpret_cast<uintptr_t>(fp); | 515 uintptr_t cursor = reinterpret_cast<uintptr_t>(fp); |
704 cursor += sizeof(fp); | 516 cursor += sizeof(fp); |
705 bool r = cursor >= lower_bound_ && cursor < stack_upper_; | 517 bool r = cursor >= lower_bound_ && cursor < stack_upper_; |
706 return r; | 518 return r; |
707 } | 519 } |
708 | 520 |
709 | 521 |
710 } // namespace dart | 522 } // namespace dart |
OLD | NEW |