blob: 621db01286e01b11f08c92ee015e9650b2a47e6e [file] [log] [blame]
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#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"
// 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 ProfileFunctionTable;
class ProfileCodeTable;
class RawCode;
class RawFunction;
class SampleFilter;
// Profile data related to a |Function|.
class ProfileFunction : public ZoneAllocated {
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_;
const char* Name() const;
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() {
void Tick(bool exclusive, intptr_t inclusive_serial);
static const char* KindToCString(Kind kind);
void PrintToJSONArray(JSONArray* functions);
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 {
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_; }
uword pc_;
intptr_t exclusive_ticks_;
intptr_t inclusive_ticks_;
// Profile data related to a |Code|.
class ProfileCode : public ZoneAllocated {
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() {
intptr_t inclusive_ticks() const { return inclusive_ticks_; }
void set_inclusive_ticks(intptr_t inclusive_ticks) {
inclusive_ticks_ = inclusive_ticks;
void IncInclusiveTicks() {
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);
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 {
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() {
intptr_t NumChildren() const {
return children_.length();
ProfileTrieNode* At(intptr_t i) {
return children_.At(i);
intptr_t IndexOf(ProfileTrieNode* node);
void SortChildren();
static int ProfileTrieNodeCompare(ProfileTrieNode* const* a,
ProfileTrieNode* const* b) {
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 {
enum TagOrder {
enum TrieKind {
static bool IsCodeTrie(TrieKind kind) {
return (kind == kExclusiveCode) || (kind == kInclusiveCode);
static bool IsFunctionTrie(TrieKind kind) {
return !IsCodeTrie(kind);
explicit Profile(Isolate* isolate);
// Build a filtered model using |filter| with the specified |tag_order|.
void Build(SampleFilter* filter, TagOrder tag_order, intptr_t extra_tags = 0);
// 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);
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 ProfileTrieWalker : public ValueObject {
explicit ProfileTrieWalker(Profile* profile)
: profile_(profile),
code_trie_(false) {
ASSERT(profile_ != NULL);
void Reset(Profile::TrieKind trie_kind);
const char* CurrentName();
// Return the current node's peer's inclusive tick count.
intptr_t CurrentInclusiveTicks();
// Return the current node's peer's exclusive tick count.
intptr_t CurrentExclusiveTicks();
// Return the current node's tick count.
intptr_t CurrentNodeTickCount();
// Return the number siblings (including yourself).
intptr_t SiblingCount();
bool Down();
bool NextSibling();
Profile* profile_;
ProfileTrieNode* parent_;
ProfileTrieNode* current_;
bool code_trie_;
class ProfilerService : public AllStatic {
enum {
kNoExtraTags = 0,
kCodeTransitionTagsBit = (1 << 0),
static void PrintJSON(JSONStream* stream,
Profile::TagOrder tag_order,
intptr_t extra_tags);
static void PrintAllocationJSON(JSONStream* stream,
Profile::TagOrder tag_order,
const Class& cls);
static void ClearSamples();
static void PrintJSONImpl(Thread* thread,
JSONStream* stream,
Profile::TagOrder tag_order,
intptr_t extra_tags,
SampleFilter* filter);
} // namespace dart