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
22namespace dart {
23
24// Forward declarations.
25class Code;
26class Function;
27class JSONArray;
28class JSONStream;
29class ProfileFunctionTable;
30class ProfileCodeTable;
31class SampleFilter;
32class ProcessedSample;
33class ProcessedSampleBuffer;
34class Profile;
35
36class 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
54class 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|.
130class 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
209class 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|.
226class 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
321class 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.
365class 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 PrintHeaderJSON(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
421class 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