1 | // Copyright (c) 2015, 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 | #ifndef RUNTIME_VM_PROFILER_SERVICE_H_ |
6 | #define RUNTIME_VM_PROFILER_SERVICE_H_ |
7 | |
8 | #include "platform/text_buffer.h" |
9 | #include "vm/allocation.h" |
10 | #include "vm/code_observers.h" |
11 | #include "vm/globals.h" |
12 | #include "vm/growable_array.h" |
13 | #include "vm/object.h" |
14 | #include "vm/profiler.h" |
15 | #include "vm/tags.h" |
16 | #include "vm/thread_interrupter.h" |
17 | #include "vm/token_position.h" |
18 | |
19 | // CPU Profile model and service protocol bits. |
20 | // NOTE: For sampling and stack walking related code, see profiler.h. |
21 | |
22 | namespace dart { |
23 | |
24 | // Forward declarations. |
25 | class Code; |
26 | class Function; |
27 | class JSONArray; |
28 | class JSONStream; |
29 | class ProfileFunctionTable; |
30 | class ProfileCodeTable; |
31 | class SampleFilter; |
32 | class ProcessedSample; |
33 | class ProcessedSampleBuffer; |
34 | class Profile; |
35 | |
36 | class ProfileFunctionSourcePosition { |
37 | public: |
38 | explicit ProfileFunctionSourcePosition(TokenPosition token_pos); |
39 | |
40 | void Tick(bool exclusive); |
41 | |
42 | TokenPosition token_pos() const { return token_pos_; } |
43 | intptr_t exclusive_ticks() const { return exclusive_ticks_; } |
44 | intptr_t inclusive_ticks() const { return inclusive_ticks_; } |
45 | |
46 | private: |
47 | TokenPosition token_pos_; |
48 | intptr_t exclusive_ticks_; |
49 | intptr_t inclusive_ticks_; |
50 | |
51 | DISALLOW_ALLOCATION(); |
52 | }; |
53 | |
54 | class ProfileCodeInlinedFunctionsCache : public ZoneAllocated { |
55 | public: |
56 | ProfileCodeInlinedFunctionsCache() : cache_cursor_(0), last_hit_(0) { |
57 | for (intptr_t i = 0; i < kCacheSize; i++) { |
58 | cache_[i].Reset(); |
59 | } |
60 | cache_hit_ = 0; |
61 | cache_miss_ = 0; |
62 | } |
63 | |
64 | ~ProfileCodeInlinedFunctionsCache() { |
65 | if (FLAG_trace_profiler) { |
66 | intptr_t total = cache_hit_ + cache_miss_; |
67 | OS::PrintErr("LOOKUPS: %" Pd " HITS: %" Pd " MISSES: %" Pd "\n" , total, |
68 | cache_hit_, cache_miss_); |
69 | } |
70 | } |
71 | |
72 | void Get(uword pc, |
73 | const Code& code, |
74 | ProcessedSample* sample, |
75 | intptr_t frame_index, |
76 | // Outputs: |
77 | GrowableArray<const Function*>** inlined_functions, |
78 | GrowableArray<TokenPosition>** inlined_token_positions, |
79 | TokenPosition* token_position); |
80 | |
81 | private: |
82 | bool FindInCache(uword pc, |
83 | intptr_t offset, |
84 | GrowableArray<const Function*>** inlined_functions, |
85 | GrowableArray<TokenPosition>** inlined_token_positions, |
86 | TokenPosition* token_position); |
87 | |
88 | // Add to cache and fill in outputs. |
89 | void Add(uword pc, |
90 | const Code& code, |
91 | ProcessedSample* sample, |
92 | intptr_t frame_index, |
93 | // Outputs: |
94 | GrowableArray<const Function*>** inlined_functions, |
95 | GrowableArray<TokenPosition>** inlined_token_positions, |
96 | TokenPosition* token_position); |
97 | |
98 | intptr_t NextFreeIndex() { |
99 | cache_cursor_ = (cache_cursor_ + 1) % kCacheSize; |
100 | return cache_cursor_; |
101 | } |
102 | |
103 | intptr_t OffsetForPC(uword pc, |
104 | const Code& code, |
105 | ProcessedSample* sample, |
106 | intptr_t frame_index); |
107 | struct CacheEntry { |
108 | void Reset() { |
109 | pc = 0; |
110 | offset = 0; |
111 | inlined_functions.Clear(); |
112 | inlined_token_positions.Clear(); |
113 | } |
114 | uword pc; |
115 | intptr_t offset; |
116 | GrowableArray<const Function*> inlined_functions; |
117 | GrowableArray<TokenPosition> inlined_token_positions; |
118 | TokenPosition token_position; |
119 | }; |
120 | |
121 | static const intptr_t kCacheSize = 128; |
122 | intptr_t cache_cursor_; |
123 | intptr_t last_hit_; |
124 | CacheEntry cache_[kCacheSize]; |
125 | intptr_t cache_miss_; |
126 | intptr_t cache_hit_; |
127 | }; |
128 | |
129 | // Profile data related to a |Function|. |
130 | class ProfileFunction : public ZoneAllocated { |
131 | public: |
132 | enum Kind { |
133 | kDartFunction, // Dart function. |
134 | kNativeFunction, // Synthetic function for Native (C/C++). |
135 | kTagFunction, // Synthetic function for a VM or User tag. |
136 | kStubFunction, // Synthetic function for stub code. |
137 | kUnknownFunction, // A singleton function for unknown objects. |
138 | }; |
139 | |
140 | ProfileFunction(Kind kind, |
141 | const char* name, |
142 | const Function& function, |
143 | const intptr_t table_index); |
144 | |
145 | const char* name() const { |
146 | ASSERT(name_ != NULL); |
147 | return name_; |
148 | } |
149 | |
150 | const char* Name() const; |
151 | |
152 | const Function* function() const { return &function_; } |
153 | |
154 | // Returns the resolved_url for the script containing this function. |
155 | const char* ResolvedScriptUrl() const; |
156 | |
157 | bool is_visible() const; |
158 | |
159 | intptr_t table_index() const { return table_index_; } |
160 | |
161 | Kind kind() const { return kind_; } |
162 | |
163 | intptr_t exclusive_ticks() const { return exclusive_ticks_; } |
164 | intptr_t inclusive_ticks() const { return inclusive_ticks_; } |
165 | |
166 | void IncInclusiveTicks() { inclusive_ticks_++; } |
167 | |
168 | void Tick(bool exclusive, |
169 | intptr_t inclusive_serial, |
170 | TokenPosition token_position); |
171 | |
172 | static const char* KindToCString(Kind kind); |
173 | |
174 | void PrintToJSONArray(JSONArray* functions); |
175 | |
176 | // Returns true if the call was successful and |pfsp| is set. |
177 | bool GetSinglePosition(ProfileFunctionSourcePosition* pfsp); |
178 | |
179 | void TickSourcePosition(TokenPosition token_position, bool exclusive); |
180 | |
181 | intptr_t NumSourcePositions() const { |
182 | return source_position_ticks_.length(); |
183 | } |
184 | |
185 | const ProfileFunctionSourcePosition& GetSourcePosition(intptr_t i) const { |
186 | return source_position_ticks_.At(i); |
187 | } |
188 | |
189 | private: |
190 | const Kind kind_; |
191 | const char* name_; |
192 | const Function& function_; |
193 | const intptr_t table_index_; |
194 | ZoneGrowableArray<intptr_t> profile_codes_; |
195 | ZoneGrowableArray<ProfileFunctionSourcePosition> source_position_ticks_; |
196 | |
197 | intptr_t exclusive_ticks_; |
198 | intptr_t inclusive_ticks_; |
199 | intptr_t inclusive_serial_; |
200 | |
201 | void PrintToJSONObject(JSONObject* func); |
202 | // A |ProfileCode| that contains this function. |
203 | void AddProfileCode(intptr_t code_table_index); |
204 | |
205 | friend class ProfileCode; |
206 | friend class ProfileBuilder; |
207 | }; |
208 | |
209 | class ProfileCodeAddress { |
210 | public: |
211 | explicit ProfileCodeAddress(uword pc); |
212 | |
213 | void Tick(bool exclusive); |
214 | |
215 | uword pc() const { return pc_; } |
216 | intptr_t exclusive_ticks() const { return exclusive_ticks_; } |
217 | intptr_t inclusive_ticks() const { return inclusive_ticks_; } |
218 | |
219 | private: |
220 | uword pc_; |
221 | intptr_t exclusive_ticks_; |
222 | intptr_t inclusive_ticks_; |
223 | }; |
224 | |
225 | // Profile data related to a |Code|. |
226 | class ProfileCode : public ZoneAllocated { |
227 | public: |
228 | enum Kind { |
229 | kDartCode, // Live Dart code. |
230 | kCollectedCode, // Dead Dart code. |
231 | kNativeCode, // Native code. |
232 | kReusedCode, // Dead Dart code that has been reused by new kDartCode. |
233 | kTagCode, // A special kind of code representing a tag. |
234 | }; |
235 | |
236 | ProfileCode(Kind kind, |
237 | uword start, |
238 | uword end, |
239 | int64_t timestamp, |
240 | const AbstractCode code); |
241 | |
242 | Kind kind() const { return kind_; } |
243 | |
244 | uword start() const { return start_; } |
245 | void set_start(uword start) { start_ = start; } |
246 | |
247 | uword end() const { return end_; } |
248 | void set_end(uword end) { end_ = end; } |
249 | |
250 | void ExpandLower(uword start); |
251 | void ExpandUpper(uword end); |
252 | void TruncateLower(uword start); |
253 | void TruncateUpper(uword end); |
254 | |
255 | bool Contains(uword pc) const { return (pc >= start_) && (pc < end_); } |
256 | |
257 | bool Overlaps(const ProfileCode* other) const; |
258 | |
259 | int64_t compile_timestamp() const { return compile_timestamp_; } |
260 | void set_compile_timestamp(int64_t timestamp) { |
261 | compile_timestamp_ = timestamp; |
262 | } |
263 | |
264 | intptr_t exclusive_ticks() const { return exclusive_ticks_; } |
265 | void set_exclusive_ticks(intptr_t exclusive_ticks) { |
266 | exclusive_ticks_ = exclusive_ticks; |
267 | } |
268 | void IncExclusiveTicks() { exclusive_ticks_++; } |
269 | |
270 | intptr_t inclusive_ticks() const { return inclusive_ticks_; } |
271 | void set_inclusive_ticks(intptr_t inclusive_ticks) { |
272 | inclusive_ticks_ = inclusive_ticks; |
273 | } |
274 | void IncInclusiveTicks() { inclusive_ticks_++; } |
275 | |
276 | bool IsOptimizedDart() const; |
277 | const AbstractCode code() const { return code_; } |
278 | |
279 | const char* name() const { return name_; } |
280 | void SetName(const char* name); |
281 | void GenerateAndSetSymbolName(const char* prefix); |
282 | |
283 | static const char* KindToCString(Kind kind); |
284 | |
285 | void PrintToJSONArray(JSONArray* codes); |
286 | |
287 | ProfileFunction* function() const { return function_; } |
288 | |
289 | intptr_t code_table_index() const { return code_table_index_; } |
290 | |
291 | private: |
292 | void Tick(uword pc, bool exclusive, intptr_t serial); |
293 | void TickAddress(uword pc, bool exclusive); |
294 | |
295 | ProfileFunction* SetFunctionAndName(ProfileFunctionTable* table); |
296 | |
297 | void PrintNativeCode(JSONObject* profile_code_obj); |
298 | void PrintCollectedCode(JSONObject* profile_code_obj); |
299 | void PrintOverwrittenCode(JSONObject* profile_code_obj); |
300 | void PrintTagCode(JSONObject* profile_code_obj); |
301 | |
302 | void set_code_table_index(intptr_t index) { code_table_index_ = index; } |
303 | |
304 | const Kind kind_; |
305 | uword start_; |
306 | uword end_; |
307 | intptr_t exclusive_ticks_; |
308 | intptr_t inclusive_ticks_; |
309 | intptr_t inclusive_serial_; |
310 | |
311 | const AbstractCode code_; |
312 | char* name_; |
313 | int64_t compile_timestamp_; |
314 | ProfileFunction* function_; |
315 | intptr_t code_table_index_; |
316 | ZoneGrowableArray<ProfileCodeAddress> address_ticks_; |
317 | |
318 | friend class ProfileBuilder; |
319 | }; |
320 | |
321 | class ProfileCodeTable : public ZoneAllocated { |
322 | public: |
323 | ProfileCodeTable() : table_(8) {} |
324 | |
325 | intptr_t length() const { return table_.length(); } |
326 | |
327 | ProfileCode* At(intptr_t index) const { |
328 | ASSERT(index >= 0); |
329 | ASSERT(index < length()); |
330 | return table_[index]; |
331 | } |
332 | |
333 | // Find the table index to the ProfileCode containing pc. |
334 | // Returns < 0 if not found. |
335 | intptr_t FindCodeIndexForPC(uword pc) const; |
336 | |
337 | ProfileCode* FindCodeForPC(uword pc) const { |
338 | intptr_t index = FindCodeIndexForPC(pc); |
339 | if (index < 0) { |
340 | return NULL; |
341 | } |
342 | return At(index); |
343 | } |
344 | |
345 | // Insert |new_code| into the table. Returns the table index where |new_code| |
346 | // was inserted. Will merge with an overlapping ProfileCode if one is present. |
347 | intptr_t InsertCode(ProfileCode* new_code); |
348 | |
349 | private: |
350 | void FindNeighbors(uword pc, |
351 | intptr_t* lo, |
352 | intptr_t* hi, |
353 | ProfileCode** lo_code, |
354 | ProfileCode** hi_code) const; |
355 | |
356 | void VerifyOrder(); |
357 | void VerifyOverlap(); |
358 | |
359 | ZoneGrowableArray<ProfileCode*> table_; |
360 | }; |
361 | |
362 | |
363 | // The model for a profile. Most of the model is zone allocated, therefore |
364 | // a zone must be created that lives longer than this object. |
365 | class Profile : public ValueObject { |
366 | public: |
367 | explicit Profile(Isolate* isolate); |
368 | |
369 | // Build a filtered model using |filter|. |
370 | void Build(Thread* thread, SampleFilter* filter, SampleBuffer* sample_buffer); |
371 | |
372 | // After building: |
373 | int64_t min_time() const { return min_time_; } |
374 | int64_t max_time() const { return max_time_; } |
375 | int64_t GetTimeSpan() const { return max_time() - min_time(); } |
376 | intptr_t sample_count() const { return sample_count_; } |
377 | ProcessedSample* SampleAt(intptr_t index); |
378 | |
379 | intptr_t NumFunctions() const; |
380 | |
381 | ProfileFunction* GetFunction(intptr_t index); |
382 | ProfileCode* GetCode(intptr_t index); |
383 | ProfileCode* GetCodeFromPC(uword pc, int64_t timestamp); |
384 | |
385 | void PrintProfileJSON(JSONStream* stream, bool include_code_samples); |
386 | |
387 | ProfileFunction* FindFunction(const Function& function); |
388 | |
389 | private: |
390 | void (JSONObject* obj); |
391 | void ProcessSampleFrameJSON(JSONArray* stack, |
392 | ProfileCodeInlinedFunctionsCache* cache, |
393 | ProcessedSample* sample, |
394 | intptr_t frame_index); |
395 | void ProcessInlinedFunctionFrameJSON(JSONArray* stack, |
396 | const Function* inlined_function); |
397 | void PrintFunctionFrameIndexJSON(JSONArray* stack, ProfileFunction* function); |
398 | void PrintCodeFrameIndexJSON(JSONArray* stack, |
399 | ProcessedSample* sample, |
400 | intptr_t frame_index); |
401 | void PrintSamplesJSON(JSONObject* obj, bool code_samples); |
402 | |
403 | Isolate* isolate_; |
404 | Zone* zone_; |
405 | ProcessedSampleBuffer* samples_; |
406 | ProfileCodeTable* live_code_; |
407 | ProfileCodeTable* dead_code_; |
408 | ProfileCodeTable* tag_code_; |
409 | ProfileFunctionTable* functions_; |
410 | intptr_t dead_code_index_offset_; |
411 | intptr_t tag_code_index_offset_; |
412 | |
413 | int64_t min_time_; |
414 | int64_t max_time_; |
415 | |
416 | intptr_t sample_count_; |
417 | |
418 | friend class ProfileBuilder; |
419 | }; |
420 | |
421 | class ProfilerService : public AllStatic { |
422 | public: |
423 | static void PrintJSON(JSONStream* stream, |
424 | int64_t time_origin_micros, |
425 | int64_t time_extent_micros, |
426 | bool include_code_samples); |
427 | |
428 | static void PrintAllocationJSON(JSONStream* stream, |
429 | const Class& cls, |
430 | int64_t time_origin_micros, |
431 | int64_t time_extent_micros); |
432 | |
433 | static void PrintNativeAllocationJSON(JSONStream* stream, |
434 | int64_t time_origin_micros, |
435 | int64_t time_extent_micros, |
436 | bool include_code_samples); |
437 | |
438 | static void ClearSamples(); |
439 | |
440 | private: |
441 | static void PrintJSONImpl(Thread* thread, |
442 | JSONStream* stream, |
443 | SampleFilter* filter, |
444 | SampleBuffer* sample_buffer, |
445 | bool include_code_samples); |
446 | }; |
447 | |
448 | } // namespace dart |
449 | |
450 | #endif // RUNTIME_VM_PROFILER_SERVICE_H_ |
451 | |