Index: base/tracked_objects.h |
=================================================================== |
--- base/tracked_objects.h (revision 107970) |
+++ base/tracked_objects.h (working copy) |
@@ -16,8 +16,13 @@ |
#include "base/time.h" |
#include "base/synchronization/lock.h" |
#include "base/threading/thread_local_storage.h" |
+#include "base/tracking_info.h" |
#include "base/values.h" |
+#if defined(OS_WIN) |
+#include <mmsystem.h> // Declare timeGetTime(); |
+#endif |
+ |
// TrackedObjects provides a database of stats about objects (generally Tasks) |
// that are tracked. Tracking means their birth, death, duration, birth thread, |
// death thread, and birth place are recorded. This data is carefully spread |
@@ -69,7 +74,7 @@ |
// any locks, as all that data is constant across the life of the process. |
// |
// The above work *could* also be done for any other object as well by calling |
-// TallyABirthIfActive() and TallyADeathIfActive() as appropriate. |
+// TallyABirthIfActive() and TallyRunOnNamedThreadIfTracking() as appropriate. |
// |
// The amount of memory used in the above data structures depends on how many |
// threads there are, and how many Locations of construction there are. |
@@ -167,6 +172,96 @@ |
namespace tracked_objects { |
//------------------------------------------------------------------------------ |
+ |
+#define USE_FAST_TIME_CLASS_FOR_DURATION_CALCULATIONS |
+ |
+#if defined(USE_FAST_TIME_CLASS_FOR_DURATION_CALCULATIONS) |
+ |
+// TimeTicks maintains a wasteful 64 bits of data (we need less than 32), and on |
+// windows, a 64 bit timer is expensive to even obtain. We use a simple |
+// millisecond counter for most of our time values, as well as millisecond units |
+// of duration between those values. This means we can only handle durations |
+// up to 49 days (range), or 24 days (non-negative time durations). |
+// We only define enough methods to service the needs of the tracking classes, |
+// and our interfaces are modeled after what TimeTicks and TimeDelta use (so we |
+// can swap them into place if we want to use the "real" classes). |
+ |
+class BASE_EXPORT Duration { // Similar to base::TimeDelta. |
+ public: |
+ Duration() : ms_(0) {} |
+ |
+ Duration& operator+=(const Duration& other) { |
+ ms_ += other.ms_; |
+ return *this; |
+ } |
+ |
+ Duration operator+(const Duration& other) const { |
+ return Duration(ms_ + other.ms_); |
+ } |
+ |
+ bool operator==(const Duration& other) const { return ms_ == other.ms_; } |
+ bool operator!=(const Duration& other) const { return ms_ != other.ms_; } |
+ bool operator>(const Duration& other) const { return ms_ > other.ms_; } |
+ |
+ static Duration FromMilliseconds(int ms) { return Duration(ms); } |
+ |
+ int32 InMilliseconds() const { return ms_; } |
+ |
+ private: |
+ friend class TrackedTime; |
+ explicit Duration(int32 duration) : ms_(duration) {} |
+ |
+ // Internal time is stored directly in milliseconds. |
+ int32 ms_; |
+}; |
+ |
+class BASE_EXPORT TrackedTime { // Similar to base::TimeTicks. |
+ public: |
+ TrackedTime() : ms_(0) {} |
+ explicit TrackedTime(const base::TimeTicks& time) |
+ : ms_((time - base::TimeTicks()).InMilliseconds()) { |
+ } |
+ |
+ static TrackedTime Now() { |
+#if defined(OS_WIN) |
+ // Use lock-free accessor to 32 bit time. |
+ // Note that TimeTicks::Now() is built on this, so we have "compatible" |
+ // times when we down-convert a TimeTicks sample. |
+ // TODO(jar): Surface this interface via something in base/time.h. |
+ return TrackedTime(static_cast<int32>(::timeGetTime())); |
+#else |
+ // Posix has nice cheap 64 bit times, so we just down-convert it. |
+ return TrackedTime(base::TimeTicks::Now()); |
+#endif // OS_WIN |
+ } |
+ |
+ Duration operator-(const TrackedTime& other) const { |
+ return Duration(ms_ - other.ms_); |
+ } |
+ |
+ TrackedTime operator+(const Duration& other) const { |
+ return TrackedTime(ms_ + other.ms_); |
+ } |
+ |
+ bool is_null() const { return ms_ == 0; } |
+ |
+ private: |
+ friend class Duration; |
+ explicit TrackedTime(int32 ms) : ms_(ms) {} |
+ |
+ // Internal duration is stored directly in milliseconds. |
+ uint32 ms_; |
+}; |
+ |
+#else |
+ |
+// Just use full 64 bit time calculations, and the slower TimeTicks::Now(). |
+typedef base::TimeTicks TrackedTime; |
+typedef base::TimeDelta Duration; |
+ |
+#endif // USE_FAST_TIME_CLASS_FOR_DURATION_CALCULATIONS |
+ |
+//------------------------------------------------------------------------------ |
// For a specific thread, and a specific birth place, the collection of all |
// death info (with tallies for each death thread, to prevent access conflicts). |
class ThreadData; |
@@ -217,8 +312,8 @@ |
}; |
//------------------------------------------------------------------------------ |
-// Basic info summarizing multiple destructions of an object with a single |
-// birthplace (fixed Location). Used both on specific threads, and also used |
+// Basic info summarizing multiple destructions of a tracked object with a |
+// single birthplace (fixed Location). Used both on specific threads, and also |
// in snapshots when integrating assembled data. |
class BASE_EXPORT DeathData { |
@@ -233,14 +328,14 @@ |
// Update stats for a task destruction (death) that had a Run() time of |
// |duration|, and has had a queueing delay of |queue_duration|. |
- void RecordDeath(const base::TimeDelta& queue_duration, |
- const base::TimeDelta& run_duration); |
+ void RecordDeath(const Duration& queue_duration, |
+ const Duration& run_duration); |
// Metrics accessors. |
int count() const { return count_; } |
- base::TimeDelta run_duration() const { return run_duration_; } |
+ Duration run_duration() const { return run_duration_; } |
int AverageMsRunDuration() const; |
- base::TimeDelta queue_duration() const { return queue_duration_; } |
+ Duration queue_duration() const { return queue_duration_; } |
int AverageMsQueueDuration() const; |
// Accumulate metrics from other into this. This method is never used on |
@@ -250,7 +345,7 @@ |
// Simple print of internal state for use in line of HTML. |
void WriteHTML(std::string* output) const; |
- // Constructe a DictionaryValue instance containing all our stats. The caller |
+ // Construct a DictionaryValue instance containing all our stats. The caller |
// assumes ownership of the returned instance. |
base::DictionaryValue* ToValue() const; |
@@ -259,8 +354,8 @@ |
private: |
int count_; // Number of destructions. |
- base::TimeDelta run_duration_; // Sum of all Run()time durations. |
- base::TimeDelta queue_duration_; // Sum of all queue time durations. |
+ Duration run_duration_; // Sum of all Run()time durations. |
+ Duration queue_duration_; // Sum of all queue time durations. |
}; |
//------------------------------------------------------------------------------ |
@@ -287,13 +382,11 @@ |
const std::string DeathThreadName() const; |
int count() const { return death_data_.count(); } |
- base::TimeDelta run_duration() const { return death_data_.run_duration(); } |
+ Duration run_duration() const { return death_data_.run_duration(); } |
+ Duration queue_duration() const { return death_data_.queue_duration(); } |
int AverageMsRunDuration() const { |
return death_data_.AverageMsRunDuration(); |
} |
- base::TimeDelta queue_duration() const { |
- return death_data_.queue_duration(); |
- } |
int AverageMsQueueDuration() const { |
return death_data_.AverageMsQueueDuration(); |
} |
@@ -305,8 +398,6 @@ |
// The caller assumes ownership of the memory in the returned instance. |
base::DictionaryValue* ToValue() const; |
- void Add(const Snapshot& other); |
- |
private: |
const BirthOnThread* birth_; // Includes Location and birth_thread. |
const ThreadData* death_thread_; |
@@ -326,20 +417,23 @@ |
DataCollector(); |
~DataCollector(); |
- // Add all stats from the indicated thread into our arrays. This function is |
- // mutex protected, and *could* be called from any threads (although current |
- // implementation serialized calls to Append). |
+ // Adds all stats from the indicated thread into our arrays. This function |
+ // uses locks at the lowest level (when accessing the underlying maps which |
+ // could change when not locked), and can be called from any threads. |
void Append(const ThreadData& thread_data); |
// After the accumulation phase, the following accessor is used to process the |
- // data. |
+ // data (i.e., sort it, filter it, etc.). |
Collection* collection(); |
- // After collection of death data is complete, we can add entries for all the |
- // remaining living objects. |
+ // Adds entries for all the remaining living objects (objects that have |
+ // tallied a birth, but have not yet tallied a matching death, and hence must |
+ // be either running, queued up, or being held in limbo for future posting). |
+ // This should be called after all known ThreadData instances have been |
+ // processed using Append(). |
void AddListOfLivingObjects(); |
- // Generate a ListValue representation of the vector of snapshots. The caller |
+ // Generates a ListValue representation of the vector of snapshots. The caller |
// assumes ownership of the memory in the returned instance. |
base::ListValue* ToValue() const; |
@@ -350,7 +444,8 @@ |
Collection collection_; |
// The total number of births recorded at each location for which we have not |
- // seen a death count. |
+ // seen a death count. This map changes as we do Append() calls, and is later |
+ // used by AddListOfLivingObjects() to gather up unaccounted for births. |
BirthCount global_birth_count_; |
DISALLOW_COPY_AND_ASSIGN(DataCollector); |
@@ -359,6 +454,9 @@ |
//------------------------------------------------------------------------------ |
// Aggregation contains summaries (totals and subtotals) of groups of Snapshot |
// instances to provide printing of these collections on a single line. |
+// We generally provide an aggregate total for the entire list, as well as |
+// aggregate subtotals for groups of stats (example: group of all lives that |
+// died on the specific thread). |
class BASE_EXPORT Aggregation: public DeathData { |
public: |
@@ -414,6 +512,7 @@ |
// Imediate action keywords. |
RESET_ALL_DATA = -1, |
+ UNKNOWN_KEYWORD = -2, |
}; |
explicit Comparator(); |
@@ -470,6 +569,10 @@ |
// members of the tested elements. |
enum Selector selector_; |
+ // Translate a path keyword into a selector. This is a slow implementation, |
+ // but this is rarely done, and only for HTML presentations. |
+ static Selector FindSelector(const std::string& keyword); |
+ |
// For filtering into acceptable and unacceptable snapshot instance, the |
// following is required to be a substring of the selector_ field. |
std::string required_; |
@@ -493,16 +596,27 @@ |
//------------------------------------------------------------------------------ |
// For each thread, we have a ThreadData that stores all tracking info generated |
// on this thread. This prevents the need for locking as data accumulates. |
+// We use ThreadLocalStorage to quickly identfy the current ThreadData context. |
+// We also have a linked list of ThreadData instances, and that list is used to |
+// harvest data from all existing instances. |
class BASE_EXPORT ThreadData { |
public: |
+ // Current allowable states of the tracking system. The states can vary |
+ // between ACTIVE and DEACTIVATED, but can never go back to UNINITIALIZED. |
+ enum Status { |
+ UNINITIALIZED, |
+ ACTIVE, |
+ DEACTIVATED, |
+ }; |
+ |
typedef std::map<Location, Births*> BirthMap; |
typedef std::map<const Births*, DeathData> DeathMap; |
// Initialize the current thread context with a new instance of ThreadData. |
- // This is used by all threads that have names, and can be explicitly |
- // set *before* any births are threads have taken place. It is generally |
- // only used by the message loop, which has a well defined name. |
+ // This is used by all threads that have names, and should be explicitly |
+ // set *before* any births on the threads have taken place. It is generally |
+ // only used by the message loop, which has a well defined thread name. |
static void InitializeThreadContext(const std::string& suggested_name); |
// Using Thread Local Store, find the current instance for collecting data. |
@@ -515,33 +629,47 @@ |
// append to output. |
static void WriteHTML(const std::string& query, std::string* output); |
- // Constructe a ListValue instance containing all recursive results in our |
- // process. The caller assumes ownership of the memory in the returned |
- // instance. The |process_type| should become an enum, which corresponds |
- // to all possible process types. I'm using an int as a placeholder. |
- static base::Value* ToValue(int process_type); |
- |
// For a given accumulated array of results, use the comparator to sort and |
// subtotal, writing the results to the output. |
static void WriteHTMLTotalAndSubtotals( |
const DataCollector::Collection& match_array, |
const Comparator& comparator, std::string* output); |
- // Find (or create) a place to count births from the given location in this |
+ // Constructs a DictionaryValue instance containing all recursive results in |
+ // our process. The caller assumes ownership of the memory in the returned |
+ // instance. |
+ static base::DictionaryValue* ToValue(); |
+ |
+ // Finds (or creates) a place to count births from the given location in this |
// thread, and increment that tally. |
// TallyABirthIfActive will returns NULL if the birth cannot be tallied. |
static Births* TallyABirthIfActive(const Location& location); |
+ // Records the end of a timed run of an object. The |completed_task| contains |
+ // a pointer to a Births, the time_posted, and a delayed_start_time if any. |
+ // The |start_of_run| indicates when we started to perform the run of the |
+ // task. The delayed_start_time is non-null for tasks that were posted as |
+ // delayed tasks, and it indicates when the task should have run (i.e., when |
+ // it should have posted out of the timer queue, and into the work queue. |
+ // The |end_of_run| was just obtained by a call to Now() (just after the task |
+ // finished). It is provided as an argument to help with testing. |
+ static void TallyRunOnNamedThreadIfTracking( |
+ const base::TrackingInfo& completed_task, |
+ const TrackedTime& start_of_run, |
+ const TrackedTime& end_of_run); |
+ |
// Record the end of a timed run of an object. The |birth| is the record for |
- // the instance, the |time_posted| and |start_of_run| are times of posting |
- // into a message loop queue, and of starting to perform the run of the task. |
+ // the instance, the |time_posted| records that instant, which is presumed to |
+ // be when the task was posted into a queue to run on a worker thread. |
+ // The |start_of_run| is when the worker thread started to perform the run of |
+ // the task. |
// The |end_of_run| was just obtained by a call to Now() (just after the task |
// finished). |
- static void TallyADeathIfActive(const Births* birth, |
- const base::TimeTicks& time_posted, |
- const base::TimeTicks& delayed_start_time, |
- const base::TimeTicks& start_of_run, |
- const base::TimeTicks& end_of_run); |
+ static void TallyRunOnWorkerThreadIfTracking( |
+ const Births* birth, |
+ const TrackedTime& time_posted, |
+ const TrackedTime& start_of_run, |
+ const TrackedTime& end_of_run); |
const std::string thread_name() const { return thread_name_; } |
@@ -567,34 +695,34 @@ |
// bogus counts VERY rarely. |
static void ResetAllThreadData(); |
- // Set internal status_ to either become ACTIVE, or later, to be SHUTDOWN, |
+ // Initializes all statics if needed (this initialization call should be made |
+ // while we are single threaded). Returns false if unable to initialize. |
+ static bool Initialize(); |
+ |
+ // Sets internal status_ to either become ACTIVE, or DEACTIVATED, |
// based on argument being true or false respectively. |
- // IF tracking is not compiled in, this function will return false. |
- static bool StartTracking(bool status); |
- static bool IsActive(); |
+ // If tracking is not compiled in, this function will return false. |
+ static bool InitializeAndSetTrackingStatus(bool status); |
+ static bool tracking_status(); |
// Provide a time function that does nothing (runs fast) when we don't have |
// the profiler enabled. It will generally be optimized away when it is |
// ifdef'ed to be small enough (allowing the profiler to be "compiled out" of |
// the code). |
- static base::TimeTicks Now(); |
+ static TrackedTime Now(); |
- // WARNING: ONLY call this function when you are running single threaded |
- // (again) and all message loops and threads have terminated. Until that |
- // point some threads may still attempt to write into our data structures. |
- // Delete recursively all data structures, starting with the list of |
+ // Cleans up data structures, and returns statics to near pristine (mostly |
+ // uninitialized) state. If there is any chance that other threads are still |
+ // using the data structures, then the |leak| argument should be passed in as |
+ // true, and the data structures (birth maps, death maps, ThreadData |
+ // insntances, etc.) will be leaked and not deleted. If you have joined all |
+ // threads since the time that InitializeAndSetTrackingStatus() was called, |
+ // then you can pass in a |leak| value of false, and this function will |
+ // delete recursively all data structures, starting with the list of |
// ThreadData instances. |
- static void ShutdownSingleThreadedCleanup(); |
+ static void ShutdownSingleThreadedCleanup(bool leak); |
private: |
- // Current allowable states of the tracking system. The states always |
- // proceed towards SHUTDOWN, and never go backwards. |
- enum Status { |
- UNINITIALIZED, |
- ACTIVE, |
- SHUTDOWN, |
- }; |
- |
typedef std::stack<const ThreadData*> ThreadDataPool; |
// Worker thread construction creates a name since there is none. |
@@ -614,8 +742,8 @@ |
// Find a place to record a death on this thread. |
void TallyADeath(const Births& birth, |
- const base::TimeDelta& queue_duration, |
- const base::TimeDelta& duration); |
+ const Duration& queue_duration, |
+ const Duration& duration); |
// Using our lock to protect the iteration, Clear all birth and death data. |
void Reset(); |
@@ -633,16 +761,31 @@ |
// Link to the most recently created instance (starts a null terminated list). |
// The list is traversed by about:tracking when it needs to snapshot data. |
+ // This is only accessed while list_lock_ is held. |
static ThreadData* all_thread_data_list_head_; |
// Set of ThreadData instances for use with worker threads. When a worker |
// thread is done (terminating), we push it into this pool. When a new worker |
// thread is created, we first try to re-use a ThreadData instance from the |
// pool, and if none are available, construct a new one. |
+ // This is only accessed while list_lock_ is held. |
static ThreadDataPool* unregistered_thread_data_pool_; |
+ // The next available thread number. This should only be accessed when the |
+ // list_lock_ is held. |
+ static int thread_number_counter_; |
+ // Incarnation sequence number, indicating how many times (during unittests) |
+ // we've either transitioned out of UNINITIALIZED, or into that state. This |
+ // value is only accessed while the list_lock_ is held. |
+ static int incarnation_counter_; |
// Protection for access to all_thread_data_list_head_, and to |
- // unregistered_thread_data_pool_. |
- static base::Lock list_lock_; |
+ // unregistered_thread_data_pool_. This lock is leaked at shutdown. |
+ static base::Lock* list_lock_; |
+ // Record of what the incarnation_counter_ was when this instance was created. |
+ // If the incarnation_counter_ has changed, then we avoid pushing into the |
+ // pool (this is only critical in tests which go through multiple |
+ // incarations). |
+ int incarnation_count_for_pool_; |
+ |
// We set status_ to SHUTDOWN when we shut down the tracking service. |
static Status status_; |
@@ -679,10 +822,6 @@ |
// writing is only done from this thread. |
mutable base::Lock lock_; |
- // The next available thread number. This should only be accessed when the |
- // list_lock_ is held. |
- static int thread_number_counter_; |
- |
DISALLOW_COPY_AND_ASSIGN(ThreadData); |
}; |
@@ -695,21 +834,17 @@ |
class BASE_EXPORT AutoTracking { |
public: |
AutoTracking() { |
- if (state_ != kNeverBeenRun) |
- return; |
- ThreadData::StartTracking(true); |
- state_ = kRunning; |
+ ThreadData::Initialize(); |
} |
~AutoTracking() { |
+ // TODO(jar): Consider emitting a CSV dump of the data at this point. This |
+ // should be called after the message loops have all terminated (or at least |
+ // the main message loop is gone), so there is little chance for additional |
+ // tasks to be Run. |
} |
private: |
- enum State { |
- kNeverBeenRun, |
- kRunning, |
- }; |
- static State state_; |
DISALLOW_COPY_AND_ASSIGN(AutoTracking); |
}; |