diff options
author | jar@chromium.org <jar@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-12-30 07:31:45 +0000 |
---|---|---|
committer | jar@chromium.org <jar@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-12-30 07:31:45 +0000 |
commit | 75b7920402c6f834376a18aa1927b223c6e3eadb (patch) | |
tree | c0a770a9b85fcb09fb988ba2ff0c2eef8b994b98 /base/tracked_objects.cc | |
parent | d3d98bc21e6fe0ea6aa0186194347a1f5e4d7be8 (diff) | |
download | chromium_src-75b7920402c6f834376a18aa1927b223c6e3eadb.zip chromium_src-75b7920402c6f834376a18aa1927b223c6e3eadb.tar.gz chromium_src-75b7920402c6f834376a18aa1927b223c6e3eadb.tar.bz2 |
Provide a quick and dirty way to reset about:objects data
To make it easier to use the about:object profiling facility,
I put in a quick/dirty way to reset all profile stats to 0 (as if
there were no births, deaths, etc.). This code is only activated
under debug builds (or if a developer inists in a private build).
These stats don't impact semantics of the browser, so the hackish
approach to clearing the data counts can't instigate a crash, and
it makes it much easier to look at changes in the stats.
While changing the code, I also added a lot of comments, and did
a few minor cleanups items.
I also officially added about:tasks as a replacement for about:objects,
as this is really how the service is used in Chrome.
r=mbelshe
Review URL: http://codereview.chromium.org/100297
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@35372 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/tracked_objects.cc')
-rw-r--r-- | base/tracked_objects.cc | 104 |
1 files changed, 90 insertions, 14 deletions
diff --git a/base/tracked_objects.cc b/base/tracked_objects.cc index 0d68703..a51c0da 100644 --- a/base/tracked_objects.cc +++ b/base/tracked_objects.cc @@ -62,7 +62,6 @@ void DeathData::Clear() { } //------------------------------------------------------------------------------ - BirthOnThread::BirthOnThread(const Location& location) : location_(location), birth_thread_(ThreadData::current()) { } @@ -70,7 +69,7 @@ BirthOnThread::BirthOnThread(const Location& location) //------------------------------------------------------------------------------ Births::Births(const Location& location) : BirthOnThread(location), - birth_count_(0) { } + birth_count_(1) { } //------------------------------------------------------------------------------ // ThreadData maintains the central data for all births and death. @@ -178,7 +177,36 @@ void ThreadData::WriteHTML(const std::string& query, std::string* output) { comparator.Clear(); // Delete tiebreaker_ instances. - output->append("</pre></body></html>"); + output->append("</pre>"); + + const char* help_string = "The following are the keywords that can be used to" + "sort and aggregate the data, or to select data.<br><ul>" + "<li><b>count</b> Number of instances seen." + "<li><b>duration</b> Duration in ms from construction to descrution." + "<li><b>birth</b> Thread on which the task was constructed." + "<li><b>death</b> Thread on which the task was run and deleted." + "<li><b>file</b> File in which the task was contructed." + "<li><b>function</b> Function in which the task was constructed." + "<li><b>line</b> Line number of the file in which the task was constructed." + "</ul><br>" + "As examples:<ul>" + "<li><b>about:tasks/file</b> would sort the above data by file, and" + " aggregate data on a per-file basis." + "<li><b>about:tasks/file=Dns</b> would only list data for tasks constructed" + " in a file containing the text |Dns|." + "<li><b>about:tasks/birth/death</b> would sort the above list by birth" + " thread, and then by death thread, and would aggregate data for each pair" + " of lifetime events." + "</ul>" + " The data can be reset to zero (discarding all births, deaths, etc.) using" + " <b>about:tasks/reset</b>. The existing stats will be displayed, but the" + " internal stats will be set to zero, and start accumulating afresh. This" + " option is very helpful if you only wish to consider tasks created after" + " some point in time.<br><br>" + "If you wish to monitor Renderer events, be sure to run in --single-process" + " mode."; + output->append(help_string); + output->append("</body></html>"); } // static @@ -222,15 +250,17 @@ void ThreadData::WriteHTMLTotalAndSubtotals( } } -Births* ThreadData::FindLifetime(const Location& location) { +Births* ThreadData::TallyABirth(const Location& location) { if (!message_loop_) // In case message loop wasn't yet around... message_loop_ = MessageLoop::current(); // Find it now. BirthMap::iterator it = birth_map_.find(location); - if (it != birth_map_.end()) + if (it != birth_map_.end()) { + it->second->RecordBirth(); return it->second; - Births* tracker = new Births(location); + } + Births* tracker = new Births(location); // Lock since the map may get relocated now, and other threads sometimes // snapshot it (but they lock before copying it). AutoLock lock(lock_); @@ -267,7 +297,7 @@ const std::string ThreadData::ThreadName() const { // This may be called from another thread. void ThreadData::SnapshotBirthMap(BirthMap *output) const { - AutoLock lock(*const_cast<Lock*>(&lock_)); + AutoLock lock(lock_); for (BirthMap::const_iterator it = birth_map_.begin(); it != birth_map_.end(); ++it) (*output)[it->first] = it->second; @@ -275,13 +305,34 @@ void ThreadData::SnapshotBirthMap(BirthMap *output) const { // This may be called from another thread. void ThreadData::SnapshotDeathMap(DeathMap *output) const { - AutoLock lock(*const_cast<Lock*>(&lock_)); + AutoLock lock(lock_); for (DeathMap::const_iterator it = death_map_.begin(); it != death_map_.end(); ++it) (*output)[it->first] = it->second; } +// static +void ThreadData::ResetAllThreadData() { + ThreadData* my_list = ThreadData::current()->first(); + + for (ThreadData* thread_data = my_list; + thread_data; + thread_data = thread_data->next()) + thread_data->Reset(); +} + +void ThreadData::Reset() { + AutoLock lock(lock_); + for (DeathMap::iterator it = death_map_.begin(); + it != death_map_.end(); ++it) + it->second.Clear(); + for (BirthMap::iterator it = birth_map_.begin(); + it != birth_map_.end(); ++it) + it->second->Clear(); +} + #ifdef OS_WIN +// TODO(jar): This should use condition variables, and be cross platform. void ThreadData::RunOnAllThreads(void (*function)()) { ThreadData* list = first(); // Get existing list. @@ -395,7 +446,7 @@ void ThreadData::ShutdownDisablingFurtherTracking() { ThreadData::ThreadSafeDownCounter::ThreadSafeDownCounter(size_t count) : remaining_count_(count) { - DCHECK(remaining_count_ > 0); + DCHECK_GT(remaining_count_, 0u); } bool ThreadData::ThreadSafeDownCounter::LastCaller() { @@ -467,6 +518,7 @@ void Snapshot::Add(const Snapshot& other) { DataCollector::DataCollector() { DCHECK(ThreadData::IsActive()); + // Get an unchanging copy of a ThreadData list. ThreadData* my_list = ThreadData::current()->first(); count_of_contributing_threads_ = 0; @@ -478,6 +530,14 @@ DataCollector::DataCollector() { // Gather data serially. A different constructor could be used to do in // parallel, and then invoke an OnCompletion task. + // This hackish approach *can* get some slighly corrupt tallies, as we are + // grabbing values without the protection of a lock, but it has the advantage + // of working even with threads that don't have message loops. If a user + // sees any strangeness, they can always just run their stats gathering a + // second time. + // TODO(jar): Provide version that gathers stats safely via PostTask in all + // cases where thread_data supplies a message_loop to post to. Be careful to + // handle message_loops that are destroyed!?! for (ThreadData* thread_data = my_list; thread_data; thread_data = thread_data->next()) { @@ -670,6 +730,8 @@ bool Comparator::operator()(const Snapshot& left, break; case AVERAGE_DURATION: + if (!left.count() || !right.count()) + break; if (left.AverageMsDuration() != right.AverageMsDuration()) return left.AverageMsDuration() > right.AverageMsDuration(); break; @@ -807,10 +869,13 @@ void Comparator::SetSubgroupTiebreaker(Selector selector) { } void Comparator::ParseKeyphrase(const std::string& key_phrase) { - static std::map<const std::string, Selector> key_map; + typedef std::map<const std::string, Selector> KeyMap; + static KeyMap key_map; static bool initialized = false; if (!initialized) { initialized = true; + // Sorting and aggretation keywords, which specify how to sort the data, or + // can specify a required match from the specified field in the record. key_map["count"] = COUNT; key_map["duration"] = AVERAGE_DURATION; key_map["birth"] = BIRTH_THREAD; @@ -818,20 +883,31 @@ void Comparator::ParseKeyphrase(const std::string& key_phrase) { key_map["file"] = BIRTH_FILE; key_map["function"] = BIRTH_FUNCTION; key_map["line"] = BIRTH_LINE; + + // Immediate commands that do not involve setting sort order. + key_map["reset"] = RESET_ALL_DATA; } std::string required; + // Watch for: "sort_key=value" as we parse. size_t equal_offset = key_phrase.find('=', 0); - if (key_phrase.npos != equal_offset) + if (key_phrase.npos != equal_offset) { + // There is a value that must be matched for the data to display. required = key_phrase.substr(equal_offset + 1, key_phrase.npos); + } std::string keyword(key_phrase.substr(0, equal_offset)); keyword = StringToLowerASCII(keyword); - if (key_map.end() == key_map.find(keyword)) - return; - SetTiebreaker(key_map[keyword], required); + KeyMap::iterator it = key_map.find(keyword); + if (key_map.end() == it) + return; // Unknown keyword. + if (it->second == RESET_ALL_DATA) + ThreadData::ResetAllThreadData(); + else + SetTiebreaker(key_map[keyword], required); } bool Comparator::ParseQuery(const std::string& query) { + // Parse each keyphrase between consecutive slashes. for (size_t i = 0; i < query.size();) { size_t slash_offset = query.find('/', i); ParseKeyphrase(query.substr(i, slash_offset - i)); |