Index: runtime/vm/profiler_service.h |
diff --git a/runtime/vm/profiler_service.h b/runtime/vm/profiler_service.h |
index 19f01b622fee85975a79325066fce04e0ae34411..62612bb1e23232eb54f47c3647c07974dfaad19b 100644 |
--- a/runtime/vm/profiler_service.h |
+++ b/runtime/vm/profiler_service.h |
@@ -8,20 +8,266 @@ |
#include "vm/allocation.h" |
#include "vm/code_observers.h" |
#include "vm/globals.h" |
+#include "vm/growable_array.h" |
+#include "vm/object.h" |
#include "vm/tags.h" |
#include "vm/thread_interrupter.h" |
-// Profile VM Service. |
+// CPU Profile model and service protocol bits. |
// NOTE: For sampling and stack walking related code, see profiler.h. |
namespace dart { |
// Forward declarations. |
+class Code; |
+class Function; |
class JSONArray; |
class JSONStream; |
-class ProfilerCodeRegionTable; |
+class ProfileFunctionTable; |
+class ProfileCodeTable; |
+class RawCode; |
+class RawFunction; |
+class SampleFilter; |
-class ProfilerService : public AllStatic { |
+// Profile data related to a |Function|. |
+class ProfileFunction : public ZoneAllocated { |
+ public: |
+ enum Kind { |
+ kDartFunction, // Dart function. |
+ kNativeFunction, // Synthetic function for Native (C/C++). |
+ kTagFunction, // Synthetic function for a VM or User tag. |
+ kStubFunction, // Synthetic function for stub code. |
+ kUnknownFunction, // A singleton function for unknown objects. |
+ }; |
+ |
+ ProfileFunction(Kind kind, |
+ const char* name, |
+ const Function& function, |
+ const intptr_t table_index); |
+ |
+ const char* name() const { |
+ ASSERT(name_ != NULL); |
+ return name_; |
+ } |
+ |
+ RawFunction* function() const { |
+ return function_.raw(); |
+ } |
+ |
+ intptr_t table_index() const { |
+ return table_index_; |
+ } |
+ |
+ Kind kind() const { |
+ return kind_; |
+ } |
+ |
+ intptr_t exclusive_ticks() const { return exclusive_ticks_; } |
+ intptr_t inclusive_ticks() const { return inclusive_ticks_; } |
+ |
+ void IncInclusiveTicks() { |
+ inclusive_ticks_++; |
+ } |
+ |
+ void Tick(bool exclusive, intptr_t inclusive_serial); |
+ |
+ static const char* KindToCString(Kind kind); |
+ |
+ void PrintToJSONArray(JSONArray* functions); |
+ |
+ private: |
+ const Kind kind_; |
+ const char* name_; |
+ const Function& function_; |
+ const intptr_t table_index_; |
+ ZoneGrowableArray<intptr_t> profile_codes_; |
+ |
+ intptr_t exclusive_ticks_; |
+ intptr_t inclusive_ticks_; |
+ intptr_t inclusive_serial_; |
+ |
+ void PrintToJSONObject(JSONObject* func); |
+ // A |ProfileCode| that contains this function. |
+ void AddProfileCode(intptr_t code_table_index); |
+ |
+ friend class ProfileCode; |
+ friend class ProfileBuilder; |
+}; |
+ |
+ |
+class ProfileCodeAddress { |
+ public: |
+ explicit ProfileCodeAddress(uword pc); |
+ |
+ void Tick(bool exclusive); |
+ |
+ uword pc() const { return pc_; } |
+ intptr_t exclusive_ticks() const { return exclusive_ticks_; } |
+ intptr_t inclusive_ticks() const { return inclusive_ticks_; } |
+ |
+ private: |
+ uword pc_; |
+ intptr_t exclusive_ticks_; |
+ intptr_t inclusive_ticks_; |
+}; |
+ |
+ |
+// Profile data related to a |Code|. |
+class ProfileCode : public ZoneAllocated { |
+ public: |
+ enum Kind { |
+ kDartCode, // Live Dart code. |
+ kCollectedCode, // Dead Dart code. |
+ kNativeCode, // Native code. |
+ kReusedCode, // Dead Dart code that has been reused by new kDartCode. |
+ kTagCode, // A special kind of code representing a tag. |
+ }; |
+ |
+ ProfileCode(Kind kind, |
+ uword start, |
+ uword end, |
+ int64_t timestamp, |
+ const Code& code); |
+ |
+ Kind kind() const { return kind_; } |
+ |
+ uword start() const { return start_; } |
+ void set_start(uword start) { start_ = start; } |
+ |
+ uword end() const { return end_; } |
+ void set_end(uword end) { end_ = end; } |
+ |
+ void AdjustExtent(uword start, uword end); |
+ |
+ bool Contains(uword pc) const { |
+ return (pc >= start_) && (pc < end_); |
+ } |
+ |
+ bool Overlaps(const ProfileCode* other) const; |
+ |
+ int64_t compile_timestamp() const { return compile_timestamp_; } |
+ void set_compile_timestamp(int64_t timestamp) { |
+ compile_timestamp_ = timestamp; |
+ } |
+ |
+ intptr_t exclusive_ticks() const { return exclusive_ticks_; } |
+ void set_exclusive_ticks(intptr_t exclusive_ticks) { |
+ exclusive_ticks_ = exclusive_ticks; |
+ } |
+ void IncExclusiveTicks() { |
+ exclusive_ticks_++; |
+ } |
+ |
+ intptr_t inclusive_ticks() const { return inclusive_ticks_; } |
+ void set_inclusive_ticks(intptr_t inclusive_ticks) { |
+ inclusive_ticks_ = inclusive_ticks; |
+ } |
+ void IncInclusiveTicks() { |
+ inclusive_ticks_++; |
+ } |
+ |
+ bool IsOptimizedDart() const; |
+ RawCode* code() const { |
+ return code_.raw(); |
+ } |
+ |
+ const char* name() const { return name_; } |
+ void SetName(const char* name); |
+ void GenerateAndSetSymbolName(const char* prefix); |
+ |
+ static const char* KindToCString(Kind kind); |
+ |
+ void PrintToJSONArray(JSONArray* codes); |
+ |
+ private: |
+ void Tick(uword pc, bool exclusive, intptr_t serial); |
+ void TickAddress(uword pc, bool exclusive); |
+ |
+ ProfileFunction* SetFunctionAndName(ProfileFunctionTable* table); |
+ |
+ ProfileFunction* function() const { |
+ return function_; |
+ } |
+ |
+ void PrintNativeCode(JSONObject* profile_code_obj); |
+ void PrintCollectedCode(JSONObject* profile_code_obj); |
+ void PrintOverwrittenCode(JSONObject* profile_code_obj); |
+ void PrintTagCode(JSONObject* profile_code_obj); |
+ |
+ void set_code_table_index(intptr_t index) { code_table_index_ = index; } |
+ intptr_t code_table_index() const { return code_table_index_; } |
+ |
+ const Kind kind_; |
+ uword start_; |
+ uword end_; |
+ intptr_t exclusive_ticks_; |
+ intptr_t inclusive_ticks_; |
+ intptr_t inclusive_serial_; |
+ |
+ const Code& code_; |
+ const char* name_; |
+ int64_t compile_timestamp_; |
+ ProfileFunction* function_; |
+ intptr_t code_table_index_; |
+ ZoneGrowableArray<ProfileCodeAddress> address_ticks_; |
+ |
+ friend class ProfileBuilder; |
+}; |
+ |
+ |
+// Stack traces are organized in a trie. This holds information about one node |
+// in the trie. A node in a tree represents a stack frame and a path in the tree |
+// represents a stack trace. Each unique stack trace appears in the tree once |
+// and each node has a count indicating how many times this has been observed. |
+// The index can be used to look up a |ProfileFunction| or |ProfileCode|. |
+// A node can have zero or more children. Depending on the kind of trie the |
+// children are callers or callees of the current node. |
+class ProfileTrieNode : public ZoneAllocated { |
+ public: |
+ explicit ProfileTrieNode(intptr_t index); |
+ virtual ~ProfileTrieNode(); |
+ |
+ virtual void PrintToJSONArray(JSONArray* array) const = 0; |
+ |
+ // Index into function or code tables. |
+ intptr_t table_index() const { return table_index_; } |
+ |
+ intptr_t count() const { return count_; } |
+ |
+ void Tick() { |
+ count_++; |
+ } |
+ |
+ intptr_t NumChildren() const { |
+ return children_.length(); |
+ } |
+ |
+ ProfileTrieNode* At(intptr_t i) { |
+ return children_.At(i); |
+ } |
+ |
+ protected: |
+ void SortChildren(); |
+ |
+ static int ProfileTrieNodeCompare(ProfileTrieNode* const* a, |
+ ProfileTrieNode* const* b) { |
+ ASSERT(a != NULL); |
+ ASSERT(b != NULL); |
+ return (*b)->count() - (*a)->count(); |
+ } |
+ |
+ |
+ intptr_t table_index_; |
+ intptr_t count_; |
+ ZoneGrowableArray<ProfileTrieNode*> children_; |
+ |
+ friend class ProfileBuilder; |
+}; |
+ |
+ |
+// The model for a profile. Most of the model is zone allocated, therefore |
+// a zone must be created that lives longer than this object. |
+class Profile : public ValueObject { |
public: |
enum TagOrder { |
kNoTags, |
@@ -31,8 +277,57 @@ class ProfilerService : public AllStatic { |
kVMUser |
}; |
+ enum TrieKind { |
+ kExclusiveCode, |
+ kExclusiveFunction, |
+ kInclusiveCode, |
+ kInclusiveFunction, |
+ kNumTrieKinds, |
+ }; |
+ |
+ explicit Profile(Isolate* isolate); |
+ |
+ // Build a filtered model using |filter| with the specified |tag_order|. |
+ void Build(SampleFilter* filter, TagOrder tag_order); |
+ |
+ // After building: |
+ int64_t min_time() const { return min_time_; } |
+ int64_t max_time() const { return max_time_; } |
+ int64_t GetTimeSpan() const { |
+ return max_time() - min_time(); |
+ } |
+ intptr_t sample_count() const { return sample_count_; } |
+ |
+ ProfileFunction* GetFunction(intptr_t index); |
+ ProfileCode* GetCode(intptr_t index); |
+ ProfileTrieNode* GetTrieRoot(TrieKind trie_kind); |
+ |
+ void PrintJSON(JSONStream* stream); |
+ |
+ private: |
+ Isolate* isolate_; |
+ ProfileCodeTable* live_code_; |
+ ProfileCodeTable* dead_code_; |
+ ProfileCodeTable* tag_code_; |
+ ProfileFunctionTable* functions_; |
+ intptr_t dead_code_index_offset_; |
+ intptr_t tag_code_index_offset_; |
+ |
+ ProfileTrieNode* roots_[kNumTrieKinds]; |
+ |
+ int64_t min_time_; |
+ int64_t max_time_; |
+ |
+ intptr_t sample_count_; |
+ |
+ friend class ProfileBuilder; |
+}; |
+ |
+ |
+class ProfilerService : public AllStatic { |
+ public: |
static void PrintJSON(JSONStream* stream, |
- TagOrder tag_order); |
+ Profile::TagOrder tag_order); |
static void ClearSamples(); |
}; |