OLD | NEW |
| (Empty) |
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 | |
OLD | NEW |