summaryrefslogtreecommitdiffstats
path: root/base/tracked_objects.cc
diff options
context:
space:
mode:
authorjar@chromium.org <jar@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-12-30 07:31:45 +0000
committerjar@chromium.org <jar@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-12-30 07:31:45 +0000
commit75b7920402c6f834376a18aa1927b223c6e3eadb (patch)
treec0a770a9b85fcb09fb988ba2ff0c2eef8b994b98 /base/tracked_objects.cc
parentd3d98bc21e6fe0ea6aa0186194347a1f5e4d7be8 (diff)
downloadchromium_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.cc104
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));