| 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);
|
| };
|
|
|