1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "net/base/load_log_util.h" 6 7 #include "base/format_macros.h" 8 #include "base/string_util.h" 9 #include "net/base/net_errors.h" 10 11 namespace net { 12 namespace { 13 14 class FormatHelper { 15 public: 16 std::string ToString(const LoadLog* log) { 17 entries_.clear(); 18 19 // Pass 1: Match the start/end of indentation blocks. Fills |entries_| 20 // with the results. 21 PopulateEntries(log); 22 23 // Pass 2: Figure out the maximum width of each column. This allows us 24 // to right-justify text within each column. 25 size_t max_time_width, max_indentation, max_type_width, max_dt_width; 26 GetMaxExtent( 27 &max_time_width, &max_indentation, &max_type_width, &max_dt_width); 28 29 // Pass 3: Assemble the string. 30 std::string result; 31 32 const int kSpacesPerIndentation = 2; 33 34 for (size_t i = 0; i < entries_.size(); ++i) { 35 if (log->num_entries_truncated() > 0 && i + 1 == entries_.size()) { 36 StringAppendF(&result, " ... Truncated %" PRIuS " entries ...\n", 37 log->num_entries_truncated()); 38 } 39 40 if (entries_[i].block_index != -1 && 41 static_cast<size_t>(entries_[i].block_index + 1) == i) { 42 // If there were no entries in between the START/END block then don't 43 // bother printing a line for END (it just adds noise, and we already 44 // show the time delta besides START anyway). 45 continue; 46 } 47 48 int indentation_spaces = entries_[i].indentation * kSpacesPerIndentation; 49 std::string entry_str = GetEntryString(i); 50 51 StringAppendF(&result, "t=%s: %s%s", 52 PadStringLeft(GetTimeString(i), max_time_width).c_str(), 53 PadStringLeft("", indentation_spaces).c_str(), 54 entry_str.c_str()); 55 56 if (entries_[i].IsBeginEvent()) { 57 // Summarize how long this block lasted. 58 int padding = ((max_indentation - entries_[i].indentation) * 59 kSpacesPerIndentation) + (max_type_width - entry_str.size()); 60 StringAppendF(&result, "%s [dt=%s]", 61 PadStringLeft("", padding).c_str(), 62 PadStringLeft(GetBlockDtString(i), max_dt_width).c_str()); 63 } 64 65 if (i + 1 != entries_.size()) 66 result += "\n"; 67 } 68 69 return result; 70 } 71 72 private: 73 struct Entry { 74 explicit Entry(const LoadLog::Entry* log_entry) 75 : log_entry(log_entry), indentation(0), block_index(-1) {} 76 77 bool IsBeginEvent() const { 78 return log_entry->type == LoadLog::Entry::TYPE_EVENT && 79 log_entry->event.phase == LoadLog::PHASE_BEGIN; 80 } 81 82 bool IsEndEvent() const { 83 return log_entry->type == LoadLog::Entry::TYPE_EVENT && 84 log_entry->event.phase == LoadLog::PHASE_END; 85 } 86 87 const LoadLog::Entry* log_entry; 88 size_t indentation; 89 int block_index; // The index of the matching start / end of block. 90 }; 91 92 void PopulateEntries(const LoadLog* log) { 93 int current_indentation = 0; 94 95 for (size_t i = 0; i < log->entries().size(); ++i) { 96 Entry entry(&log->entries()[i]); 97 98 entry.indentation = current_indentation; 99 100 if (entry.IsBeginEvent()) { 101 // Indent everything contained in this block. 102 current_indentation++; 103 } 104 105 if (entry.IsEndEvent()) { 106 int start_index = FindStartOfBlockIndex(entry); 107 if (start_index != -1) { 108 // Point the start / end of block at each other. 109 entry.block_index = start_index; 110 entries_[start_index].block_index = i; 111 112 // Restore the indentation prior to the block. 113 // (Could be more than 1 level if close of blocks are missing). 114 current_indentation = entries_[start_index].indentation; 115 entry.indentation = current_indentation; 116 } 117 } 118 119 entries_.push_back(entry); 120 } 121 } 122 123 int FindStartOfBlockIndex(const Entry& entry) { 124 DCHECK(entry.IsEndEvent()); 125 126 // Find the matching start of block by scanning backwards. 127 for (int i = entries_.size() - 1; i >= 0; --i) { 128 if (entries_[i].IsBeginEvent() && 129 entries_[i].log_entry->event.type == entry.log_entry->event.type) { 130 return i; 131 } 132 } 133 return -1; // Start not found. 134 } 135 136 void GetMaxExtent(size_t* max_time_width, 137 size_t* max_indentation, 138 size_t* max_type_width, 139 size_t* max_dt_width) { 140 *max_time_width = *max_indentation = *max_type_width = *max_dt_width = 0; 141 for (size_t i = 0; i < entries_.size(); ++i) { 142 *max_time_width = std::max(*max_time_width, GetTimeString(i).size()); 143 if (entries_[i].log_entry->type == LoadLog::Entry::TYPE_EVENT) 144 *max_type_width = std::max(*max_type_width, GetEntryString(i).size()); 145 *max_indentation = std::max(*max_indentation, entries_[i].indentation); 146 147 if (entries_[i].IsBeginEvent()) 148 *max_dt_width = std::max(*max_dt_width, GetBlockDtString(i).size()); 149 } 150 } 151 152 std::string GetBlockDtString(size_t start_index) { 153 int end_index = entries_[start_index].block_index; 154 if (end_index == -1) { 155 // Block is not closed, implicitly close it at EOF. 156 end_index = entries_.size() - 1; 157 } 158 int64 dt_ms = (entries_[end_index].log_entry->time - 159 entries_[start_index].log_entry->time).InMilliseconds(); 160 161 return Int64ToString(dt_ms); 162 } 163 164 std::string GetTimeString(size_t index) { 165 int64 t_ms = (entries_[index].log_entry->time - 166 base::TimeTicks()).InMilliseconds(); 167 return Int64ToString(t_ms); 168 } 169 170 std::string GetEntryString(size_t index) { 171 const LoadLog::Entry* entry = entries_[index].log_entry; 172 173 std::string entry_str; 174 LoadLog::EventPhase phase = LoadLog::PHASE_NONE; 175 switch (entry->type) { 176 case LoadLog::Entry::TYPE_EVENT: 177 entry_str = LoadLog::EventTypeToString(entry->event.type); 178 phase = entry->event.phase; 179 180 if (phase == LoadLog::PHASE_BEGIN && 181 index + 1 < entries_.size() && 182 static_cast<size_t>(entries_[index + 1].block_index) == index) { 183 // If this starts an empty block, we will pretend it is a PHASE_NONE 184 // so we don't print the "+" prefix. 185 phase = LoadLog::PHASE_NONE; 186 } 187 break; 188 case LoadLog::Entry::TYPE_ERROR_CODE: 189 entry_str = StringPrintf("error code: %d (%s)", 190 entry->error_code, 191 ErrorToString(entry->error_code)); 192 break; 193 case LoadLog::Entry::TYPE_STRING: 194 entry_str = StringPrintf("\"%s\"", entry->string.c_str()); 195 break; 196 case LoadLog::Entry::TYPE_STRING_LITERAL: 197 entry_str = StringPrintf("\"%s\"", entry->literal); 198 break; 199 default: 200 NOTREACHED(); 201 } 202 203 switch (phase) { 204 case LoadLog::PHASE_BEGIN: 205 return std::string("+") + entry_str; 206 case LoadLog::PHASE_END: 207 return std::string("-") + entry_str; 208 case LoadLog::PHASE_NONE: 209 return std::string(" ") + entry_str; 210 default: 211 NOTREACHED(); 212 return std::string(); 213 } 214 } 215 216 static std::string PadStringLeft(const std::string& str, size_t width) { 217 DCHECK_LE(str.size(), width); 218 std::string padding; 219 padding.resize(width - str.size(), ' '); 220 return padding + str; 221 } 222 223 std::vector<Entry> entries_; 224 }; 225 226 } // namespace 227 228 // static 229 std::string LoadLogUtil::PrettyPrintAsEventTree(const LoadLog* log) { 230 FormatHelper helper; 231 return helper.ToString(log); 232 } 233 234 } // namespace net 235