| Index: base/tracked_objects.h
|
| ===================================================================
|
| --- base/tracked_objects.h (revision 104925)
|
| +++ base/tracked_objects.h (working copy)
|
| @@ -31,9 +31,7 @@
|
| // marginal allocation cost associated with construction or destruction of
|
| // tracked objects, no locks are generally employed, and probably the largest
|
| // computational cost is associated with obtaining start and stop times for
|
| -// instances as they are created and destroyed. The introduction of worker
|
| -// threads had a slight impact on this approach, and required use of some locks
|
| -// when accessing data from the worker threads.
|
| +// instances as they are created and destroyed.
|
| //
|
| // The following describes the lifecycle of tracking an instance.
|
| //
|
| @@ -54,9 +52,10 @@
|
| // The derived Births class contains slots for recording statistics about all
|
| // instances born at the same location. Statistics currently include only the
|
| // count of instances constructed.
|
| +//
|
| // Since the base class BirthOnThread contains only constant data, it can be
|
| // freely accessed by any thread at any time (i.e., only the statistic needs to
|
| -// be handled carefully, and it is ONLY read or written by the birth thread).
|
| +// be handled carefully, and stats are updated exclusively on the birth thread).
|
| //
|
| // For Tasks, having now either constructed or found the Births instance
|
| // described above, a pointer to the Births instance is then recorded into the
|
| @@ -67,7 +66,7 @@
|
| // can find out a Task's location of birth, and thread of birth, without using
|
| // any locks, as all that data is constant across the life of the process.
|
| //
|
| -// This can also be done for any other object as well by calling
|
| +// The above work *could* also be done for any other object as well by calling
|
| // TallyABirthIfActive() and TallyADeathIfActive() as appropriate.
|
| //
|
| // The amount of memory used in the above data structures depends on how many
|
| @@ -81,10 +80,11 @@
|
| // carefully accumulated. That tallying wrties into slots (members) in a
|
| // collection of DeathData instances. For each birth place Location that is
|
| // destroyed on a thread, there is a DeathData instance to record the additional
|
| -// death count, as well as accumulate the lifetime duration of the instance as
|
| -// it is destroyed (dies). By maintaining a single place to aggregate this
|
| -// addition *only* for the given thread, we avoid the need to lock such
|
| -// DeathData instances.
|
| +// death count, as well as accumulate the run-time and queue-time durations for
|
| +// the instance as it is destroyed (dies). By maintaining a single place to
|
| +// aggregate this running sum *only* for the given thread, we avoid the need to
|
| +// lock such DeathData instances. (i.e., these accumulated stats in a DeathData
|
| +// instance are exclusively updated by the singular owning thread).
|
| //
|
| // With the above lifecycle description complete, the major remaining detail is
|
| // explaining how each thread maintains a list of DeathData instances, and of
|
| @@ -129,18 +129,26 @@
|
| // birth and death datastructures, but have local (frozen) copies of the actual
|
| // statistics (birth count, durations, etc. etc.).
|
| //
|
| -// A DataCollector is a container object that holds a set of Snapshots. A
|
| -// DataCollector can be passed from thread to thread, and each thread
|
| -// contributes to it by adding or updating Snapshot instances. DataCollector
|
| -// instances are thread safe containers which are passed to various threads to
|
| -// accumulate all Snapshot instances.
|
| +// A DataCollector is a container object that holds a set of Snapshots. The
|
| +// statistics in a snapshot are gathered asynhcronously relative to their
|
| +// ongoing updates. It is possible, though highly unlikely, that stats such
|
| +// as a 64bit counter could incorrectly recorded by this process. The advantage
|
| +// to having fast (non-atomic) updates of the data outweighs the minimal risk
|
| +// of a singular corrupt statistic snapshot (only the snapshot could be corrupt,
|
| +// not the underlying and ongoing stistic). In constrast, pointer data that is
|
| +// accessed during snapshotting is completely invariant, and hence is perfectly
|
| +// acquired (i.e., no potential corruption, and no risk of a bad memory
|
| +// reference).
|
| //
|
| // After an array of Snapshots instances are colleted into a DataCollector, they
|
| -// need to be sorted, and possibly aggregated (example: how many threads are in
|
| -// a specific consecutive set of Snapshots? What was the total birth count for
|
| -// that set? etc.). Aggregation instances collect running sums of any set of
|
| -// snapshot instances, and are used to print sub-totals in an about:tracking
|
| -// page.
|
| +// need to be prepared for display our output. We currently implement a direct
|
| +// renderin to HTML, but we will soon also have a JSON serialization as well.
|
| +
|
| +// For direct HTML display, the data must be sorted, and possibly aggregated
|
| +// (example: how many threads are in a specific consecutive set of Snapshots?
|
| +// What was the total birth count for that set? etc.). Aggregation instances
|
| +// collect running sums of any set of snapshot instances, and are used to print
|
| +// sub-totals in an about:tracking page.
|
| //
|
| // TODO(jar): I need to store DataCollections, and provide facilities for taking
|
| // the difference between two gathered DataCollections. For now, I'm just
|
| @@ -167,7 +175,7 @@
|
| const ThreadData* birth_thread() const { return birth_thread_; }
|
|
|
| private:
|
| - // File/lineno of birth. This defines the essence of the type, as the context
|
| + // File/lineno of birth. This defines the essence of the task, as the context
|
| // of the birth (construction) often tell what the item is for. This field
|
| // is const, and hence safe to access from any thread.
|
| const Location location_;
|
| @@ -213,35 +221,39 @@
|
| class BASE_EXPORT DeathData {
|
| public:
|
| // Default initializer.
|
| - DeathData() : count_(0), square_duration_(0) {}
|
| + DeathData() : count_(0) {}
|
|
|
| // When deaths have not yet taken place, and we gather data from all the
|
| // threads, we create DeathData stats that tally the number of births without
|
| // a corrosponding death.
|
| - explicit DeathData(int count) : count_(count), square_duration_(0) {}
|
| + explicit DeathData(int count) : count_(count) {}
|
|
|
| - void RecordDeath(const base::TimeDelta& duration);
|
| + // 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);
|
|
|
| // Metrics accessors.
|
| int count() const { return count_; }
|
| - base::TimeDelta life_duration() const { return life_duration_; }
|
| - int64 square_duration() const { return square_duration_; }
|
| - int AverageMsDuration() const;
|
| - double StandardDeviation() const;
|
| + base::TimeDelta run_duration() const { return run_duration_; }
|
| + int AverageMsRunDuration() const;
|
| + base::TimeDelta queue_duration() const { return queue_duration_; }
|
| + int AverageMsQueueDuration() const;
|
|
|
| - // Accumulate metrics from other into this.
|
| + // Accumulate metrics from other into this. This method is never used on
|
| + // realtime statistics, and only used in snapshots and aggregatinos.
|
| void AddDeathData(const DeathData& other);
|
|
|
| // Simple print of internal state.
|
| void Write(std::string* output) const;
|
|
|
| - // Reset all tallies to zero.
|
| + // Reset all tallies to zero. This is used as a hack on realtime data.
|
| void Clear();
|
|
|
| private:
|
| - int count_; // Number of destructions.
|
| - base::TimeDelta life_duration_; // Sum of all lifetime durations.
|
| - int64 square_duration_; // Sum of squares in milliseconds.
|
| + 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.
|
| };
|
|
|
| //------------------------------------------------------------------------------
|
| @@ -260,7 +272,6 @@
|
| // When snapshotting a birth, with no death yet, use this:
|
| Snapshot(const BirthOnThread& birth_on_thread, int count);
|
|
|
| -
|
| const ThreadData* birth_thread() const { return birth_->birth_thread(); }
|
| const Location location() const { return birth_->location(); }
|
| const BirthOnThread& birth() const { return *birth_; }
|
| @@ -269,9 +280,16 @@
|
| const std::string DeathThreadName() const;
|
|
|
| int count() const { return death_data_.count(); }
|
| - base::TimeDelta life_duration() const { return death_data_.life_duration(); }
|
| - int64 square_duration() const { return death_data_.square_duration(); }
|
| - int AverageMsDuration() const { return death_data_.AverageMsDuration(); }
|
| + base::TimeDelta run_duration() const { return death_data_.run_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();
|
| + }
|
|
|
| void Write(std::string* output) const;
|
|
|
| @@ -282,10 +300,10 @@
|
| const ThreadData* death_thread_;
|
| DeathData death_data_;
|
| };
|
| +
|
| //------------------------------------------------------------------------------
|
| // DataCollector is a container class for Snapshot and BirthOnThread count
|
| -// items. It protects the gathering under locks, so that it could be called via
|
| -// Posttask on any threads, or passed to all the target threads in parallel.
|
| +// items.
|
|
|
| class BASE_EXPORT DataCollector {
|
| public:
|
| @@ -312,12 +330,6 @@
|
| private:
|
| typedef std::map<const BirthOnThread*, int> BirthCount;
|
|
|
| - // This instance may be provided to several threads to contribute data. The
|
| - // following counter tracks how many more threads will contribute. When it is
|
| - // zero, then all asynchronous contributions are complete, and locked access
|
| - // is no longer needed.
|
| - int count_of_contributing_threads_;
|
| -
|
| // The array that we collect data into.
|
| Collection collection_;
|
|
|
| @@ -325,8 +337,6 @@
|
| // seen a death count.
|
| BirthCount global_birth_count_;
|
|
|
| - base::Lock accumulation_lock_; // Protects access during accumulation phase.
|
| -
|
| DISALLOW_COPY_AND_ASSIGN(DataCollector);
|
| };
|
|
|
| @@ -381,8 +391,10 @@
|
| BIRTH_FUNCTION = 8,
|
| BIRTH_LINE = 16,
|
| COUNT = 32,
|
| - AVERAGE_DURATION = 64,
|
| - TOTAL_DURATION = 128,
|
| + AVERAGE_RUN_DURATION = 64,
|
| + TOTAL_RUN_DURATION = 128,
|
| + AVERAGE_QUEUE_DURATION = 256,
|
| + TOTAL_QUEUE_DURATION = 512,
|
|
|
| // Imediate action keywords.
|
| RESET_ALL_DATA = -1,
|
| @@ -418,7 +430,7 @@
|
| // printed line.
|
| bool IsGroupedBy(Selector selector) const;
|
|
|
| - // Using the tiebreakers as set above, we mostly get an ordering, which
|
| + // Using the tiebreakers as set above, we mostly get an ordering, with some
|
| // equivalent groups. If those groups are displayed (rather than just being
|
| // aggregated, then the following is used to order them (within the group).
|
| void SetSubgroupTiebreaker(Selector selector);
|
| @@ -462,7 +474,6 @@
|
| bool use_tiebreaker_for_sort_only_;
|
| };
|
|
|
| -
|
| //------------------------------------------------------------------------------
|
| // 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.
|
| @@ -472,15 +483,18 @@
|
| typedef std::map<Location, Births*> BirthMap;
|
| typedef std::map<const Births*, DeathData> DeathMap;
|
|
|
| - ThreadData();
|
| - ~ThreadData();
|
| + // 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.
|
| + static void InitializeThreadContext(const std::string& suggested_name);
|
|
|
| // Using Thread Local Store, find the current instance for collecting data.
|
| // If an instance does not exist, construct one (and remember it for use on
|
| // this thread.
|
| // If shutdown has already started, and we don't yet have an instance, then
|
| // return null.
|
| - static ThreadData* current();
|
| + static ThreadData* Get();
|
|
|
| // For a given (unescaped) about:tracking query, develop resulting HTML, and
|
| // append to output.
|
| @@ -496,22 +510,33 @@
|
| Births* TallyABirth(const Location& location);
|
|
|
| // Find a place to record a death on this thread.
|
| - void TallyADeath(const Births& lifetimes, const base::TimeDelta& duration);
|
| + void TallyADeath(const Births& the_birth,
|
| + const base::TimeDelta& queue_duration,
|
| + const base::TimeDelta& duration);
|
|
|
| // Helper methods to only tally if the current thread has tracking active.
|
| //
|
| // TallyABirthIfActive will returns NULL if the birth cannot be tallied.
|
| static Births* TallyABirthIfActive(const Location& location);
|
| - static void TallyADeathIfActive(const Births* lifetimes,
|
| - const base::TimeDelta& duration);
|
|
|
| + // Record the end of a timed run of an object. The |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.
|
| + // Implied is that the run just (Now()) ended. The current_message_loop is
|
| + // optional, and only used in DEBUG mode (when supplied) to verify that the
|
| + // ThreadData has a thread name that does indeed match the given loop's
|
| + // associated thread name (in RELEASE mode, its use is compiled away).
|
| + static void TallyADeathIfActive(const Births* the_birth,
|
| + const base::TimeTicks& time_posted,
|
| + const base::TimeTicks& delayed_start_time,
|
| + const base::TimeTicks& start_of_run);
|
| +
|
| // (Thread safe) Get start of list of instances.
|
| static ThreadData* first();
|
| // Iterate through the null terminated list of instances.
|
| ThreadData* next() const { return next_; }
|
|
|
| - MessageLoop* message_loop() const { return message_loop_; }
|
| - const std::string ThreadName() const;
|
| + const std::string thread_name() const { return thread_name_; }
|
|
|
| // Using our lock, make a copy of the specified maps. These calls may arrive
|
| // from non-local threads, and are used to quickly scan data from all threads
|
| @@ -528,31 +553,17 @@
|
| // Using our lock to protect the iteration, Clear all birth and death data.
|
| void Reset();
|
|
|
| - // Using the "known list of threads" gathered during births and deaths, the
|
| - // following attempts to run the given function once all all such threads.
|
| - // Note that the function can only be run on threads which have a message
|
| - // loop!
|
| - static void RunOnAllThreads(void (*Func)());
|
| -
|
| // Set internal status_ to either become ACTIVE, or later, to be SHUTDOWN,
|
| // 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();
|
|
|
| -#ifdef OS_WIN
|
| - // WARNING: ONLY call this function when all MessageLoops are still intact for
|
| - // all registered threads. IF you call it later, you will crash.
|
| - // Note: You don't need to call it at all, and you can wait till you are
|
| - // single threaded (again) to do the cleanup via
|
| - // ShutdownSingleThreadedCleanup().
|
| - // Start the teardown (shutdown) process in a multi-thread mode by disabling
|
| - // further additions to thread database on all threads. First it makes a
|
| - // local (locked) change to prevent any more threads from registering. Then
|
| - // it Posts a Task to all registered threads to be sure they are aware that no
|
| - // more accumulation can take place.
|
| - static void ShutdownMultiThreadTracking();
|
| -#endif
|
| + // 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();
|
|
|
| // WARNING: ONLY call this function when you are running single threaded
|
| // (again) and all message loops and threads have terminated. Until that
|
| @@ -562,6 +573,18 @@
|
| static void ShutdownSingleThreadedCleanup();
|
|
|
| private:
|
| + // Worker thread construction creates a name.
|
| + ThreadData();
|
| + // Message loop based construction should provide a name.
|
| + explicit ThreadData(const std::string& suggested_name);
|
| +
|
| + ~ThreadData();
|
| +
|
| + // Enter a new instance into Thread Local Store.
|
| + // Return the instance, or null if we can't register it (because we're
|
| + // shutting down).
|
| + static ThreadData* RegisterCurrentContext(ThreadData* unregistered);
|
| +
|
| // Current allowable states of the tracking system. The states always
|
| // proceed towards SHUTDOWN, and never go backwards.
|
| enum Status {
|
| @@ -570,17 +593,6 @@
|
| SHUTDOWN,
|
| };
|
|
|
| -#if defined(OS_WIN)
|
| - class ThreadSafeDownCounter;
|
| - class RunTheStatic;
|
| -#endif
|
| -
|
| - // Each registered thread is called to set status_ to SHUTDOWN.
|
| - // This is done redundantly on every registered thread because it is not
|
| - // protected by a mutex. Running on all threads guarantees we get the
|
| - // notification into the memory cache of all possible threads.
|
| - static void ShutdownDisablingFurtherTracking();
|
| -
|
| // We use thread local store to identify which ThreadData to interact with.
|
| static base::ThreadLocalStorage::Slot tls_index_;
|
|
|
| @@ -589,10 +601,7 @@
|
| // Protection for access to first_.
|
| static base::Lock list_lock_;
|
|
|
| - // We set status_ to SHUTDOWN when we shut down the tracking service. This
|
| - // setting is redundantly established by all participating threads so that we
|
| - // are *guaranteed* (without locking) that all threads can "see" the status
|
| - // and avoid additional calls into the service.
|
| + // We set status_ to SHUTDOWN when we shut down the tracking service.
|
| static Status status_;
|
|
|
| // Link to next instance (null terminated list). Used to globally track all
|
| @@ -600,10 +609,9 @@
|
| // data).
|
| ThreadData* next_;
|
|
|
| - // The message loop where tasks needing to access this instance's private data
|
| - // should be directed. Since some threads have no message loop, some
|
| - // instances have data that can't be (safely) modified externally.
|
| - MessageLoop* message_loop_;
|
| + // The name of the thread that is being recorded. If this thread has no
|
| + // message_loop, then this is a worker thread, with a sequence number postfix.
|
| + std::string thread_name_;
|
|
|
| // A map used on each thread to keep track of Births on this thread.
|
| // This map should only be accessed on the thread it was constructed on.
|
| @@ -625,10 +633,13 @@
|
| // 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);
|
| };
|
|
|
| -
|
| //------------------------------------------------------------------------------
|
| // Provide simple way to to start global tracking, and to tear down tracking
|
| // when done. Note that construction and destruction of this object must be
|
|
|