Index: base/tracked_objects.cc |
=================================================================== |
--- base/tracked_objects.cc (revision 110116) |
+++ base/tracked_objects.cc (working copy) |
@@ -8,7 +8,6 @@ |
#include "base/format_macros.h" |
#include "base/message_loop.h" |
-#include "base/string_util.h" |
#include "base/stringprintf.h" |
#include "base/threading/thread_restrictions.h" |
#include "build/build_config.h" |
@@ -54,18 +53,6 @@ |
run_time_.AddData(other.run_time_); |
} |
-void DeathData::WriteHTML(std::string* output) const { |
- if (!count_) |
- return; |
- base::StringAppendF(output, "%s:%d, ", |
- (count_ == 1) ? "Life" : "Lives", count_); |
- output->append("Run:"); |
- run_time_.WriteHTML(count_, output); |
- |
- output->append("Queue:"); |
- queue_time_.WriteHTML(count_, output); |
-} |
- |
base::DictionaryValue* DeathData::ToValue() const { |
base::DictionaryValue* dictionary = new base::DictionaryValue; |
dictionary->Set("count", base::Value::CreateIntegerValue(count_)); |
@@ -88,21 +75,6 @@ |
//------------------------------------------------------------------------------ |
-void DeathData::Data::WriteHTML(int count, std::string* output) const { |
- // Be careful to leave static_casts intact, as the type returned by |
- // InMilliseconds() may not always be an int, even if it can generally fit |
- // into an int. |
- base::StringAppendF(output, "%dms", |
- static_cast<int>(duration_)); |
- if (count == 1) { |
- output->append(" "); |
- return; |
- } |
- base::StringAppendF(output, "(%dms/life,max:%dms) ", |
- static_cast<int>(AverageMsDuration(count)), |
- static_cast<int>(max_)); |
-} |
- |
void DeathData::Data::AddData(const Data& other) { |
duration_ += other.duration_; |
if (max_ > other.max_) |
@@ -270,116 +242,6 @@ |
} |
// static |
-void ThreadData::WriteHTML(const std::string& query, std::string* output) { |
- if (status_ == UNINITIALIZED) |
- return; // Not yet initialized. |
- |
- DataCollector collected_data; // Gather data. |
- collected_data.AddListOfLivingObjects(); // Add births that are still alive. |
- |
- // Data Gathering is complete. Now to sort/process/render. |
- DataCollector::Collection* collection = collected_data.collection(); |
- |
- // Create filtering and sort comparison object. |
- Comparator comparator; |
- comparator.ParseQuery(query); |
- |
- // Filter out acceptable (matching) instances. |
- DataCollector::Collection match_array; |
- for (DataCollector::Collection::iterator it = collection->begin(); |
- it != collection->end(); ++it) { |
- if (comparator.Acceptable(*it)) |
- match_array.push_back(*it); |
- } |
- |
- comparator.Sort(&match_array); |
- |
- WriteHTMLTotalAndSubtotals(match_array, comparator, output); |
- |
- comparator.Clear(); // Delete tiebreaker_ instances. |
- |
- 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> Average duration in ms of Run() time." |
- "<li><b>TotalDuration</b> Summed durations in ms of Run() times." |
- "<li><b>MaxDuration</b> Largest duration in ms of Run() times." |
- "<li><b>AverageQueueDuration</b> Average duration in ms of queueing time." |
- "<li><b>TotalQueueDuration</b> Summed queuing durations in ms." |
- "<li><b>MaxQueueDuration</b> Largest duration in ms of queueing times." |
- "<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:tracking/file</b> would sort the above data by file, and" |
- " aggregate data on a per-file basis." |
- "<li><b>about:tracking/file=Dns</b> would only list data for tasks" |
- " constructed in a file containing the text |Dns|." |
- "<li><b>about:tracking/death/duration</b> would sort the data by death" |
- " thread(i.e., where tasks ran) and then by the average runtime for the" |
- " tasks. Form an aggregation group, one per thread, showing the results on" |
- " each thread." |
- "<li><b>about:tracking/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:tracking/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); |
-} |
- |
-// static |
-void ThreadData::WriteHTMLTotalAndSubtotals( |
- const DataCollector::Collection& match_array, |
- const Comparator& comparator, |
- std::string* output) { |
- if (match_array.empty()) { |
- output->append("There were no tracked matches."); |
- return; |
- } |
- // Aggregate during printing |
- Aggregation totals; |
- for (size_t i = 0; i < match_array.size(); ++i) { |
- totals.AddDeathSnapshot(match_array[i]); |
- } |
- output->append("Aggregate Stats: "); |
- totals.WriteHTML(output); |
- output->append("<hr><hr>"); |
- |
- Aggregation subtotals; |
- for (size_t i = 0; i < match_array.size(); ++i) { |
- if (0 == i || !comparator.Equivalent(match_array[i - 1], |
- match_array[i])) { |
- // Print group's defining characteristics. |
- comparator.WriteSortGrouping(match_array[i], output); |
- output->append("<br><br>"); |
- } |
- comparator.WriteSnapshotHTML(match_array[i], output); |
- output->append("<br>"); |
- subtotals.AddDeathSnapshot(match_array[i]); |
- if (i + 1 >= match_array.size() || |
- !comparator.Equivalent(match_array[i], |
- match_array[i + 1])) { |
- // Print aggregate stats for the group. |
- output->append("<br>"); |
- subtotals.WriteHTML(output); |
- output->append("<br><hr><br>"); |
- subtotals.Clear(); |
- } |
- } |
-} |
- |
-// static |
base::DictionaryValue* ThreadData::ToValue() { |
DataCollector collected_data; // Gather data. |
collected_data.AddListOfLivingObjects(); // Add births that are still alive. |
@@ -535,8 +397,6 @@ |
current_thread_data->TallyADeath(*birth, queue_duration, run_duration); |
} |
- |
- |
// static |
ThreadData* ThreadData::first() { |
base::AutoLock lock(*list_lock_.Pointer()); |
@@ -708,14 +568,6 @@ |
return "Still_Alive"; |
} |
-void Snapshot::WriteHTML(std::string* output) const { |
- death_data_.WriteHTML(output); |
- base::StringAppendF(output, "%s->%s ", |
- birth_->birth_thread()->thread_name().c_str(), |
- DeathThreadName().c_str()); |
- birth_->location().Write(true, true, output); |
-} |
- |
base::DictionaryValue* Snapshot::ToValue() const { |
base::DictionaryValue* dictionary = new base::DictionaryValue; |
dictionary->Set("death_data", death_data_.ToValue()); |
@@ -792,471 +644,4 @@ |
return list; |
} |
-//------------------------------------------------------------------------------ |
-// Aggregation |
- |
-Aggregation::Aggregation() |
- : birth_count_(0) { |
-} |
- |
-Aggregation::~Aggregation() { |
-} |
- |
-void Aggregation::AddDeathSnapshot(const Snapshot& snapshot) { |
- AddBirth(snapshot.birth()); |
- death_threads_[snapshot.death_thread()]++; |
- AddDeathData(snapshot.death_data()); |
-} |
- |
-void Aggregation::AddBirths(const Births& births) { |
- AddBirth(births); |
- birth_count_ += births.birth_count(); |
-} |
-void Aggregation::AddBirth(const BirthOnThread& birth) { |
- AddBirthPlace(birth.location()); |
- birth_threads_[birth.birth_thread()]++; |
-} |
- |
-void Aggregation::AddBirthPlace(const Location& location) { |
- locations_[location]++; |
- birth_files_[location.file_name()]++; |
-} |
- |
-void Aggregation::WriteHTML(std::string* output) const { |
- if (locations_.size() == 1) { |
- locations_.begin()->first.Write(true, true, output); |
- } else { |
- base::StringAppendF(output, "%" PRIuS " Locations. ", locations_.size()); |
- if (birth_files_.size() > 1) { |
- base::StringAppendF(output, "%" PRIuS " Files. ", birth_files_.size()); |
- } else { |
- base::StringAppendF(output, "All born in %s. ", |
- birth_files_.begin()->first.c_str()); |
- } |
- } |
- |
- if (birth_threads_.size() > 1) { |
- base::StringAppendF(output, "%" PRIuS " BirthingThreads. ", |
- birth_threads_.size()); |
- } else { |
- base::StringAppendF(output, "All born on %s. ", |
- birth_threads_.begin()->first->thread_name().c_str()); |
- } |
- |
- if (death_threads_.size() > 1) { |
- base::StringAppendF(output, "%" PRIuS " DeathThreads. ", |
- death_threads_.size()); |
- } else { |
- if (death_threads_.begin()->first) { |
- base::StringAppendF(output, "All deleted on %s. ", |
- death_threads_.begin()->first->thread_name().c_str()); |
- } else { |
- output->append("All these objects are still alive."); |
- } |
- } |
- |
- if (birth_count_ > 1) |
- base::StringAppendF(output, "Births=%d ", birth_count_); |
- |
- DeathData::WriteHTML(output); |
-} |
- |
-void Aggregation::Clear() { |
- birth_count_ = 0; |
- birth_files_.clear(); |
- locations_.clear(); |
- birth_threads_.clear(); |
- DeathData::Clear(); |
- death_threads_.clear(); |
-} |
- |
-//------------------------------------------------------------------------------ |
-// Comparison object for sorting. |
- |
-Comparator::Comparator() |
- : selector_(NIL), |
- tiebreaker_(NULL), |
- combined_selectors_(0), |
- use_tiebreaker_for_sort_only_(false) {} |
- |
-void Comparator::Clear() { |
- if (tiebreaker_) { |
- tiebreaker_->Clear(); |
- delete tiebreaker_; |
- tiebreaker_ = NULL; |
- } |
- use_tiebreaker_for_sort_only_ = false; |
- selector_ = NIL; |
-} |
- |
-// static |
-Comparator::Selector Comparator::FindSelector(const std::string& keyword) { |
- // Sorting and aggretation keywords, which specify how to sort the data, or |
- // can specify a required match from the specified field in the record. |
- if (0 == keyword.compare("count")) |
- return COUNT; |
- if (0 == keyword.compare("totalduration")) |
- return TOTAL_RUN_DURATION; |
- if (0 == keyword.compare("duration")) |
- return AVERAGE_RUN_DURATION; |
- if (0 == keyword.compare("maxduration")) |
- return MAX_RUN_DURATION; |
- if (0 == keyword.compare("totalqueueduration")) |
- return TOTAL_QUEUE_DURATION; |
- if (0 == keyword.compare("averagequeueduration")) |
- return AVERAGE_QUEUE_DURATION; |
- if (0 == keyword.compare("maxqueueduration")) |
- return MAX_QUEUE_DURATION; |
- if (0 == keyword.compare("birth")) |
- return BIRTH_THREAD; |
- if (0 == keyword.compare("death")) |
- return DEATH_THREAD; |
- if (0 == keyword.compare("file")) |
- return BIRTH_FILE; |
- if (0 == keyword.compare("function")) |
- return BIRTH_FUNCTION; |
- if (0 == keyword.compare("line")) |
- return BIRTH_LINE; |
- if (0 == keyword.compare("reset")) |
- return RESET_ALL_DATA; |
- return UNKNOWN_KEYWORD; |
-} |
- |
-bool Comparator::operator()(const Snapshot& left, |
- const Snapshot& right) const { |
- switch (selector_) { |
- case BIRTH_THREAD: |
- if (left.birth_thread() != right.birth_thread() && |
- left.birth_thread()->thread_name() != |
- right.birth_thread()->thread_name()) |
- return left.birth_thread()->thread_name() < |
- right.birth_thread()->thread_name(); |
- break; |
- |
- case DEATH_THREAD: |
- if (left.death_thread() != right.death_thread() && |
- left.DeathThreadName() != |
- right.DeathThreadName()) { |
- if (!left.death_thread()) |
- return true; |
- if (!right.death_thread()) |
- return false; |
- return left.DeathThreadName() < |
- right.DeathThreadName(); |
- } |
- break; |
- |
- case BIRTH_FILE: |
- if (left.location().file_name() != right.location().file_name()) { |
- int comp = strcmp(left.location().file_name(), |
- right.location().file_name()); |
- if (comp) |
- return 0 > comp; |
- } |
- break; |
- |
- case BIRTH_FUNCTION: |
- if (left.location().function_name() != right.location().function_name()) { |
- int comp = strcmp(left.location().function_name(), |
- right.location().function_name()); |
- if (comp) |
- return 0 > comp; |
- } |
- break; |
- |
- case BIRTH_LINE: |
- if (left.location().line_number() != right.location().line_number()) |
- return left.location().line_number() < |
- right.location().line_number(); |
- break; |
- |
- case COUNT: |
- if (left.count() != right.count()) |
- return left.count() > right.count(); // Sort large at front of vector. |
- break; |
- |
- case AVERAGE_RUN_DURATION: |
- if (!left.count() || !right.count()) |
- break; |
- if (left.AverageMsRunDuration() != right.AverageMsRunDuration()) |
- return left.AverageMsRunDuration() > right.AverageMsRunDuration(); |
- break; |
- |
- case TOTAL_RUN_DURATION: |
- if (!left.count() || !right.count()) |
- break; |
- if (left.run_duration() != right.run_duration()) |
- return left.run_duration() > right.run_duration(); |
- break; |
- |
- case MAX_RUN_DURATION: |
- if (!left.count() || !right.count()) |
- break; |
- if (left.run_duration_max() != right.run_duration_max()) |
- return left.run_duration_max() > right.run_duration_max(); |
- break; |
- |
- case AVERAGE_QUEUE_DURATION: |
- if (!left.count() || !right.count()) |
- break; |
- if (left.AverageMsQueueDuration() != right.AverageMsQueueDuration()) |
- return left.AverageMsQueueDuration() > right.AverageMsQueueDuration(); |
- break; |
- |
- case TOTAL_QUEUE_DURATION: |
- if (!left.count() || !right.count()) |
- break; |
- if (left.queue_duration() != right.queue_duration()) |
- return left.queue_duration() > right.queue_duration(); |
- break; |
- |
- case MAX_QUEUE_DURATION: |
- if (!left.count() || !right.count()) |
- break; |
- if (left.queue_duration_max() != right.queue_duration_max()) |
- return left.queue_duration_max() > right.queue_duration_max(); |
- break; |
- |
- default: |
- break; |
- } |
- if (tiebreaker_) |
- return tiebreaker_->operator()(left, right); |
- return false; |
-} |
- |
-void Comparator::Sort(DataCollector::Collection* collection) const { |
- std::sort(collection->begin(), collection->end(), *this); |
-} |
- |
-bool Comparator::Equivalent(const Snapshot& left, |
- const Snapshot& right) const { |
- switch (selector_) { |
- case BIRTH_THREAD: |
- if (left.birth_thread() != right.birth_thread() && |
- left.birth_thread()->thread_name() != |
- right.birth_thread()->thread_name()) |
- return false; |
- break; |
- |
- case DEATH_THREAD: |
- if (left.death_thread() != right.death_thread() && |
- left.DeathThreadName() != right.DeathThreadName()) |
- return false; |
- break; |
- |
- case BIRTH_FILE: |
- if (!required_.empty()) |
- break; // No reason to aggregate when we've filtered out some. |
- if (left.location().file_name() != right.location().file_name()) { |
- int comp = strcmp(left.location().file_name(), |
- right.location().file_name()); |
- if (comp) |
- return false; |
- } |
- break; |
- |
- case BIRTH_FUNCTION: |
- if (!required_.empty()) |
- break; // No reason to aggregate when we've filtered out some. |
- if (left.location().function_name() != right.location().function_name()) { |
- int comp = strcmp(left.location().function_name(), |
- right.location().function_name()); |
- if (comp) |
- return false; |
- } |
- break; |
- |
- case COUNT: |
- case AVERAGE_RUN_DURATION: |
- case TOTAL_RUN_DURATION: |
- case MAX_RUN_DURATION: |
- case AVERAGE_QUEUE_DURATION: |
- case TOTAL_QUEUE_DURATION: |
- case MAX_QUEUE_DURATION: |
- // We don't produce separate aggretation when only counts or times differ. |
- break; |
- |
- default: |
- break; |
- } |
- if (tiebreaker_ && !use_tiebreaker_for_sort_only_) |
- return tiebreaker_->Equivalent(left, right); |
- return true; |
-} |
- |
-bool Comparator::Acceptable(const Snapshot& sample) const { |
- if (required_.size()) { |
- switch (selector_) { |
- case BIRTH_THREAD: |
- if (sample.birth_thread()->thread_name().find(required_) == |
- std::string::npos) |
- return false; |
- break; |
- |
- case DEATH_THREAD: |
- if (sample.DeathThreadName().find(required_) == std::string::npos) |
- return false; |
- break; |
- |
- case BIRTH_FILE: |
- if (!strstr(sample.location().file_name(), required_.c_str())) |
- return false; |
- break; |
- |
- case BIRTH_FUNCTION: |
- if (!strstr(sample.location().function_name(), required_.c_str())) |
- return false; |
- break; |
- |
- default: |
- break; |
- } |
- } |
- if (tiebreaker_ && !use_tiebreaker_for_sort_only_) |
- return tiebreaker_->Acceptable(sample); |
- return true; |
-} |
- |
-void Comparator::SetTiebreaker(Selector selector, const std::string& required) { |
- if (selector == selector_ || NIL == selector) |
- return; |
- combined_selectors_ |= selector; |
- if (NIL == selector_) { |
- selector_ = selector; |
- if (required.size()) |
- required_ = required; |
- return; |
- } |
- if (tiebreaker_) { |
- if (use_tiebreaker_for_sort_only_) { |
- Comparator* temp = new Comparator; |
- temp->tiebreaker_ = tiebreaker_; |
- tiebreaker_ = temp; |
- } |
- } else { |
- tiebreaker_ = new Comparator; |
- DCHECK(!use_tiebreaker_for_sort_only_); |
- } |
- tiebreaker_->SetTiebreaker(selector, required); |
-} |
- |
-bool Comparator::IsGroupedBy(Selector selector) const { |
- return 0 != (selector & combined_selectors_); |
-} |
- |
-void Comparator::SetSubgroupTiebreaker(Selector selector) { |
- if (selector == selector_ || NIL == selector) |
- return; |
- if (!tiebreaker_) { |
- use_tiebreaker_for_sort_only_ = true; |
- tiebreaker_ = new Comparator; |
- tiebreaker_->SetTiebreaker(selector, ""); |
- } else { |
- tiebreaker_->SetSubgroupTiebreaker(selector); |
- } |
-} |
- |
-void Comparator::ParseKeyphrase(const std::string& key_phrase) { |
- 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) { |
- // 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); |
- Selector selector = FindSelector(keyword); |
- if (selector == UNKNOWN_KEYWORD) |
- return; |
- if (selector == RESET_ALL_DATA) { |
- ThreadData::ResetAllThreadData(); |
- return; |
- } |
- SetTiebreaker(selector, 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)); |
- if (query.npos == slash_offset) |
- break; |
- i = slash_offset + 1; |
- } |
- |
- // Select subgroup ordering (if we want to display the subgroup) |
- SetSubgroupTiebreaker(COUNT); |
- SetSubgroupTiebreaker(AVERAGE_RUN_DURATION); |
- SetSubgroupTiebreaker(TOTAL_RUN_DURATION); |
- SetSubgroupTiebreaker(MAX_RUN_DURATION); |
- SetSubgroupTiebreaker(AVERAGE_QUEUE_DURATION); |
- SetSubgroupTiebreaker(TOTAL_QUEUE_DURATION); |
- SetSubgroupTiebreaker(MAX_QUEUE_DURATION); |
- SetSubgroupTiebreaker(BIRTH_THREAD); |
- SetSubgroupTiebreaker(DEATH_THREAD); |
- SetSubgroupTiebreaker(BIRTH_FUNCTION); |
- SetSubgroupTiebreaker(BIRTH_FILE); |
- SetSubgroupTiebreaker(BIRTH_LINE); |
- |
- return true; |
-} |
- |
-bool Comparator::WriteSortGrouping(const Snapshot& sample, |
- std::string* output) const { |
- bool wrote_data = false; |
- switch (selector_) { |
- case BIRTH_THREAD: |
- base::StringAppendF(output, "All new on %s ", |
- sample.birth_thread()->thread_name().c_str()); |
- wrote_data = true; |
- break; |
- |
- case DEATH_THREAD: |
- if (sample.death_thread()) { |
- base::StringAppendF(output, "All deleted on %s ", |
- sample.DeathThreadName().c_str()); |
- } else { |
- output->append("All still alive "); |
- } |
- wrote_data = true; |
- break; |
- |
- case BIRTH_FILE: |
- base::StringAppendF(output, "All born in %s ", |
- sample.location().file_name()); |
- break; |
- |
- case BIRTH_FUNCTION: |
- output->append("All born in "); |
- sample.location().WriteFunctionName(output); |
- output->push_back(' '); |
- break; |
- |
- default: |
- break; |
- } |
- if (tiebreaker_ && !use_tiebreaker_for_sort_only_) { |
- wrote_data |= tiebreaker_->WriteSortGrouping(sample, output); |
- } |
- return wrote_data; |
-} |
- |
-void Comparator::WriteSnapshotHTML(const Snapshot& sample, |
- std::string* output) const { |
- sample.death_data().WriteHTML(output); |
- if (!(combined_selectors_ & BIRTH_THREAD) || |
- !(combined_selectors_ & DEATH_THREAD)) |
- base::StringAppendF(output, "%s->%s ", |
- (combined_selectors_ & BIRTH_THREAD) ? "*" : |
- sample.birth().birth_thread()->thread_name().c_str(), |
- (combined_selectors_ & DEATH_THREAD) ? "*" : |
- sample.DeathThreadName().c_str()); |
- sample.birth().location().Write(!(combined_selectors_ & BIRTH_FILE), |
- !(combined_selectors_ & BIRTH_FUNCTION), |
- output); |
-} |
- |
} // namespace tracked_objects |