1#include "stats.h"
2#include <fstream>
3#include <sstream>
4#include <iomanip>
5#include <map>
6#include <framework/stdext/time.h>
7#include <framework/ui/uiwidget.h>
8#include <framework/ui/ui.h>
9
10Stats g_stats;
11
12void Stats::add(int type, Stat* stat) {
13 if (type < 0 || type > STATS_LAST)
14 return;
15 std::lock_guard<std::mutex> lock(m_mutex);
16
17 auto it = stats[type].data.emplace(stat->description, StatsData(0, 0, stat->extraDescription)).first;
18 it->second.calls += 1;
19 it->second.executionTime += stat->executionTime;
20
21 if (stat->executionTime > 1000) {
22 if (stats[type].slow.size() > 10000) {
23 delete stats[type].slow.front();
24 stats[type].slow.pop_front();
25 }
26 stats[type].slow.push_back(stat);
27 } else
28 delete stat;
29}
30
31std::string Stats::get(int type, int limit, bool pretty) {
32 if (type < 0 || type > STATS_LAST)
33 return "";
34
35 std::lock_guard<std::mutex> lock(m_mutex);
36 std::multimap<uint64_t, StatsMap::const_iterator> sorted_stats;
37
38 uint64_t total_time = 0;
39 uint64_t time_from_start = (stdext::micros() - stats[type].start);
40
41 for (auto it = stats[type].data.cbegin(); it != stats[type].data.cend(); ++it) {
42 sorted_stats.emplace(it->second.executionTime, it);
43 total_time += it->second.executionTime;
44 }
45
46 if (total_time == 0 || time_from_start == 0)
47 return "";
48
49 std::stringstream ret;
50
51 int i = 0;
52 if (pretty)
53 ret << "Function" << std::setw(42) << "Calls" << std::setw(10) << "Time (ms)" << std::setw(10) << "Time (%)" << std::setw(10) << "Cpu (%)" << "\n";
54 else
55 ret << "Stats|" << type << "|" << limit << "|" << stdext::micros() << "|" << total_time << "|" << time_from_start << "\n";
56
57 for (auto it = sorted_stats.rbegin(); it != sorted_stats.rend(); ++it) {
58 if (i++ > limit)
59 break;
60 if (pretty) {
61 std::string name = it->second->first.substr(0, 45);
62 ret << name << std::setw(50 - name.size()) << it->second->second.calls << std::setw(10) << (it->second->second.executionTime / 1000)
63 << std::setw(10) << ((it->second->second.executionTime * 100) / (total_time)) << std::setw(10) << ((it->second->second.executionTime * 100) / (time_from_start)) << "\n";
64 } else {
65 ret << it->second->first << "|" << it->second->second.calls << "|" << it->second->second.executionTime << "\n";
66 }
67 }
68
69 return ret.str();
70}
71
72void Stats::clear(int type) {
73 if (type < 0 || type > STATS_LAST)
74 return;
75 std::lock_guard<std::mutex> lock(m_mutex);
76 stats[type].start = stdext::micros();
77 stats[type].data.clear();
78}
79
80void Stats::clearAll() {
81 std::lock_guard<std::mutex> lock(m_mutex);
82 for (int i = 0; i <= STATS_LAST; ++i) {
83 stats[i].data.clear();
84 stats[i].slow.clear();
85 }
86 resetSleepTime();
87}
88
89std::string Stats::getSlow(int type, int limit, unsigned int minTime, bool pretty) {
90 if (type < 0 || type > STATS_LAST)
91 return "";
92 std::lock_guard<std::mutex> lock(m_mutex);
93
94 std::stringstream ret;
95
96 int i = 0;
97 if (pretty)
98 ret << "Function" << std::setw(42) << "Time (ms)" << std::setw(20) << "Extra description" << "\n";
99 else
100 ret << "Slow|" << type << "|" << limit << "|" << minTime << "|" << stdext::micros() << "\n";
101
102 minTime *= 1000;
103
104 for (auto it = stats[type].slow.rbegin(); it != stats[type].slow.rend(); ++it) {
105 if ((*it)->executionTime < (minTime))
106 continue;
107 if (i++ > limit)
108 break;
109 if (pretty) {
110 std::string name = (*it)->description.substr(0, 45);
111 ret << name << std::setw(50 - name.size()) << (*it)->executionTime / 1000 << std::setw(20) << (*it)->extraDescription << "\n";
112 } else {
113 ret << (*it)->description << "|" << (*it)->executionTime << "|" << (*it)->extraDescription << "\n";
114 }
115 }
116
117 return ret.str();
118}
119
120void Stats::clearSlow(int type) {
121 if (type < 0 || type > STATS_LAST)
122 return;
123 std::lock_guard<std::mutex> lock(m_mutex);
124 for (auto& stat : stats[type].slow)
125 delete stat;
126 stats[type].slow.clear();
127}
128
129
130void Stats::addWidget(UIWidget* widget)
131{
132 createdWidgets += 1;
133 widgets.insert(widget);
134}
135
136void Stats::removeWidget(UIWidget* widget)
137{
138 destroyedWidgets += 1;
139 widgets.erase(widget);
140}
141
142struct WidgetTreeNode {
143 UIWidgetPtr widget;
144 int children_count;
145 std::list<WidgetTreeNode> children;
146};
147
148void collectWidgets(WidgetTreeNode& node)
149{
150 node.widget->setRootChild(true);
151 node.children_count = node.widget->getChildren().size();
152 for (auto& child : node.widget->getChildren()) {
153 node.children.push_back(WidgetTreeNode { child, 0, {} });
154 collectWidgets(node.children.back());
155 node.children_count += node.children.back().children_count;
156 }
157}
158
159void printNode(std::stringstream& ret, WidgetTreeNode& node, int depth, int limit, bool pretty)
160{
161 if (depth >= limit || node.children_count < 50) return;
162 ret << std::string(depth, '-') << node.widget->getId() << "|" << node.children_count << "\n";
163 for (auto& child : node.children) {
164 printNode(ret, child, depth + 1, limit, pretty);
165 }
166}
167
168std::string Stats::getWidgetsInfo(int limit, bool pretty)
169{
170 int unusedWidgets = 0;
171 // let's find invalid widgets
172 for (auto& w : widgets) {
173 w->setRootChild(false);
174 }
175
176 WidgetTreeNode node{ g_ui.getRootWidget() , 0, {} };
177 collectWidgets(node);
178
179 std::map<std::string, std::pair<int, int>> unusedWidgetsMap;
180 std::map<std::string, std::pair<int, int>> allWidgetsMap;
181
182 for (auto& w : widgets) {
183 if (!w->isRootChild()) {
184 unusedWidgets += 1;
185 auto it = unusedWidgetsMap.emplace(w->getSource(), std::make_pair(0, 0)).first;
186 it->second.first += 1;
187 it->second.second += w->getUseCount();
188 }
189 auto it = allWidgetsMap.emplace(w->getSource(), std::make_pair(0, 0)).first;
190 it->second.first += 1;
191 it->second.second += w->getUseCount();
192 }
193
194 std::stringstream ret;
195 if (pretty)
196 ret << "Widgets: " << (createdWidgets - destroyedWidgets) << " (" << destroyedWidgets << "/" << createdWidgets << "/" << unusedWidgets << ")\n";
197 else
198 ret << (createdWidgets - destroyedWidgets) << "|" << destroyedWidgets << "|" << createdWidgets << "|" << unusedWidgets << "\n";
199 if (pretty)
200 ret << "Textures: " << (createdTextures - destroyedTextures) << " (" << destroyedTextures << "/" << createdTextures << ")\n";
201 else
202 ret << (createdTextures - destroyedTextures) << "|" << destroyedTextures << "|" << createdTextures << "\n";
203 if (pretty)
204 ret << "Creatures: " << (createdCreatures - destroyedCreatures) << " (" << destroyedCreatures << "/" << createdCreatures << ")\n";
205 else
206 ret << (createdCreatures - destroyedCreatures) << "|" << destroyedCreatures << "|" << createdCreatures << "\n";
207 if (pretty)
208 ret << "Things: " << (createdThings - destroyedThings) << " (" << destroyedThings << "/" << createdThings << ")\n";
209 else
210 ret << (createdThings - destroyedThings) << "|" << destroyedThings << "|" << createdThings << "\n";
211
212 ret << "Active widgets (Widget|Childerns)" << "\n";
213
214 printNode(ret, node, 0, limit, pretty);
215
216 if (pretty) {
217 ret << "\n\n" << "All Widgets (Source|Widgets|UseCount)" << "\n\n";
218 } else {
219 ret << "AllWidgets|Source|Widgets|UseCount" << "\n";
220 }
221
222 std::multimap<int, std::pair<std::string, int>> allWidgetsMapSorted;
223 for (auto& it : allWidgetsMap) {
224 if (it.second.first > 20) {
225 allWidgetsMapSorted.emplace(it.second.first, std::make_pair(it.first, it.second.second));
226 }
227 }
228
229 int i = 0;
230 for (auto it = allWidgetsMapSorted.rbegin(); it != allWidgetsMapSorted.rend(); ++it) {
231 ret << it->second.first << "|" << it->first << "|" << it->second.second << "\n";
232 if (++i >= limit) {
233 break;
234 }
235 }
236
237 if (pretty) {
238 ret << "\n\n" << "Unused Widgets (Source|Widgets|UseCount)" << "\n\n";
239 } else {
240 ret << "UnusedWidgets|Source|Widgets|UseCount" << "\n";
241 }
242
243 std::multimap<int, std::pair<std::string, int>> unusedWidgetsMapSorted;
244 for (auto& it : unusedWidgetsMap) {
245 unusedWidgetsMapSorted.emplace(it.second.first, std::make_pair(it.first, it.second.second));
246 }
247
248 i = 0;
249 for (auto it = unusedWidgetsMapSorted.rbegin(); it != unusedWidgetsMapSorted.rend(); ++it) {
250 ret << it->second.first << "|" << it->first << "|" << it->second.second << "\n";
251 if (++i >= limit) {
252 break;
253 }
254 }
255
256 return ret.str();
257}
258