| 220 | } |
| 221 | |
| 222 | void FlameGraph::printTreeFrame(Writer& out, const Trie& f, int level, const char** names) { |
| 223 | std::vector<Node> children; |
| 224 | children.reserve(f._children.size()); |
| 225 | for (auto it = f._children.begin(); it != f._children.end(); ++it) { |
| 226 | children.push_back(Node(it->first, 0, it->second)); |
| 227 | } |
| 228 | std::sort(children.begin(), children.end(), Node::orderByTotal); |
| 229 | |
| 230 | double pct = 100.0 / _root._total; |
| 231 | for (size_t i = 0; i < children.size(); i++) { |
| 232 | u32 key = children[i]._key; |
| 233 | const Trie* trie = children[i]._trie; |
| 234 | |
| 235 | u32 type = trie->type(key); |
| 236 | std::string name = names[trie->nameIndex(key)]; |
| 237 | StringUtils::replace(name, '&', "&", 5); |
| 238 | StringUtils::replace(name, '<', "<", 4); |
| 239 | StringUtils::replace(name, '>', ">", 4); |
| 240 | |
| 241 | const char* div_class = trie->_children.empty() ? " class=\"o\"" : ""; |
| 242 | |
| 243 | if (_reverse) { |
| 244 | snprintf(_buf, sizeof(_buf) - 1, |
| 245 | "<li><div%s>%.2f%% [%s]</div> <span class=\"t%d\">%s</span>\n", |
| 246 | div_class, trie->_total * pct, Format().thousands(trie->_total), |
| 247 | type, name.c_str()); |
| 248 | } else { |
| 249 | snprintf(_buf, sizeof(_buf) - 1, |
| 250 | "<li><div%s>%.2f%% [%s] • self: %.2f%% [%s]</div> <span class=\"t%d\">%s</span>\n", |
| 251 | div_class, trie->_total * pct, Format().thousands(trie->_total), |
| 252 | trie->_self * pct, Format().thousands(trie->_self), |
| 253 | type, name.c_str()); |
| 254 | } |
| 255 | out << _buf; |
| 256 | |
| 257 | if (!trie->_children.empty()) { |
| 258 | out << "<ul>\n"; |
| 259 | if (trie->_total >= _mintotal) { |
| 260 | printTreeFrame(out, *trie, level + 1, names); |
| 261 | } else { |
| 262 | out << "<li>...\n"; |
| 263 | } |
| 264 | out << "</ul>\n"; |
| 265 | } |
| 266 | } |
| 267 | } |
| 268 | |
| 269 | void FlameGraph::printCpool(Writer& out) { |
| 270 | out << "'all'"; |