Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1041)

Unified Diff: base/tracked_objects.h

Issue 8429009: Enable tracking of objects by default (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 9 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « base/threading/worker_pool_win.cc ('k') | base/tracked_objects.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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);
};
« no previous file with comments | « base/threading/worker_pool_win.cc ('k') | base/tracked_objects.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698